標籤:io os for sp on amp ad ef size
題意:給定一張n<=100,m<=1000的無向圖,另外相同權值的邊不超過10條,求最小產生樹的數目。
思路:首先我們將不同的權值從小到大分開考慮。
我們證明以下定理:一個無向圖所有的最小產生樹中某種權值的邊的數目均相同。
開始時,每個點單獨構成一個集合。
首先只考慮權值最小的邊,將它們全部添加進圖中,並去掉環,由於是全部嘗試添加,那麼只要是用這種權值的邊能夠連通的點,最終就一定能在一個集合中。
那麼不管添加的是哪些邊,最終形成的集合數都是一定的,且集合的劃分情況一定相同。那麼真正添加的邊數也是相同的。因為每添加一條邊集合的數目便減少1.
那麼權值第二小的邊呢?我們將之間得到的集合每個集合都縮為一個點,那麼權值第二小的邊就變成了當前權值最小的邊,也有上述的結論。
因此每個階段,添加的邊數都是相同的。我們以權值劃分階段,那麼也就意味著某種權值的邊的數目是完全相同的。
於是我們考慮做法。
首先做一遍最小產生樹看一下每種權值的邊出現了幾次。若不能構成產生樹輸出0.
然後考慮每一個階段:從小到大處理每一種權值的邊,狀壓枚舉所有這種權值的邊,看這種權值的邊出現指定次數時能否全部加入當前的森林。若能,則這個階段的數目+1.
那麼答案就是每個階段的數目的乘積。
對於每一個階段,我們也可以不用暴力枚舉,而用O(N^3)的Matrix-Tree定理求解行列式。若相同權值的邊數過多的話就只能用這種方法了。
Code:(狀態壓縮)
#include <map>#include <cstdio>#include <cctype>#include <cstring>#include <iostream>#include <algorithm>using namespace std;#define N 110int n, m;struct Edge {int f, t, len;void read() {scanf("%d%d%d", &f, &t, &len);}bool operator < (const Edge &B) const {return len < B.len;}}E[1010];map<int, int> M;int root[N], tmp[N];void reset() {for(int i = 1; i <= n; ++i)root[i] = i;}int find(int x) {int q = x, tq;for(; x != root[x]; x = root[x]);while(q != x) {tq = root[q];root[q] = x;q = tq;}return x;}int count(int x) {int res = 0;for(; x; x -= x & -x)++res;return res;}int _debug(int x) {return M[x];}#define Mod 31011int main() {scanf("%d%d", &n, &m);register int i, j, k;for(i = 1; i <= m; ++i)E[i].read();sort(E + 1, E + m + 1);int intree = 0, ra, rb;reset();for(i = 1; i <= m; ++i) {ra = find(E[i].f);rb = find(E[i].t);if (ra != rb) {++M[E[i].len];root[ra] = rb;if (++intree == n - 1)break;}}if (intree < n - 1) {puts("0");return 0;}int S, res = 1, now;reset();for(i = 1; i <= m; ) {for(j = i; E[j].len == E[j + 1].len; ++j);if (M[E[i].len]) {memcpy(tmp, root, sizeof root);now = 0;for(S = 1; S < (1 << (j - i + 1)); ++S) {if (count(S) != M[E[i].len])continue;memcpy(root, tmp, sizeof tmp);bool ac = 1;for(k = i; k <= j; ++k) {if ((S >> (k - i)) & 1) {ra = find(E[k].f);rb = find(E[k].t);if (ra == rb) {ac = 0;break;}root[ra] = rb;}}if (ac)++now;}res = res * now % Mod;memcpy(root, tmp, sizeof tmp);for(k = i; k <= j; ++k) {ra = find(E[k].f);rb = find(E[k].t);if (ra != rb)root[ra] = rb;}}i = j + 1;}printf("%d", res);return 0;}
BZOJ1016 [JSOI2008]最小產生樹計數