bzoj 1016[JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBDescription
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。
Input
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整
数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,0
00。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
Output
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
Sample Input
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
Sample Output
8
是一道对 kruskal 原理的考察
考虑kruskal 算法的实现过程,我们先将边权排序, 然后从小到大加边
可以发现,所有的最小生成树,每种权值的边的数量是一定了
每次进行完一组相同权值的边的操作时,图的连通性是相同的
ps: 假设在加入权值为 k 的边之前, 图的连通性相同, 每当我们通过存在一条权值为k的边时,对应的点必然会被加入到对应的集合中,所以连通性依然相同
相同权值边数量小于10, 我们可以得到之前的状态,然后DFS枚举, 最后用乘法原理得到答案
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #define LL long long 6 using namespace std; 7 8 const int MAXN = 100 + 10, MAXM = 1e3 + 10; 9 int cnt = 0; 10 int tot = 0; 11 int n, m; 12 LL sum = 0; 13 LL ans = 1; 14 LL mod = 31011; 15 int last = 1; 16 int T = 0; 17 int f[2 * MAXN]; 18 int flag[2 * MAXN]; 19 int exs[2 * MAXN]; 20 int father[2 * MAXN]; 21 int use[4 * MAXM]; 22 23 24 struct wa { 25 int l, r; 26 int num; 27 } wait[4 * MAXM]; 28 struct edge { 29 int u, v; 30 int w; 31 } e[MAXM * 4]; 32 33 void init() 34 { 35 for(int i = 1; i <= n; i++) { 36 father[i] = i; 37 } 38 } 39 40 int find(int x) 41 { 42 if(father[x] != x) { 43 return find(father[x]); 44 } 45 return x; 46 } 47 48 inline LL read() 49 { 50 LL x = 0, w = 1; char ch = 0; 51 while(ch < '0' || ch > '9') { 52 if(ch == '-') { 53 w = -1; 54 } 55 ch = getchar(); 56 } 57 while(ch >= '0' && ch <= '9') { 58 x = x * 10 + ch - '0'; 59 ch = getchar(); 60 } 61 return x * w; 62 } 63 64 bool cmp(edge a, edge b) 65 { 66 return a.w < b.w; 67 } 68 69 /*bool judge(int last, int lim) 70 { 71 for(int i = 1; i <= tot; i++) { 72 f[wait[i]] = wait[i]; 73 } 74 for(int i = last; i <= lim; i++) { 75 if(use[i]) { 76 int x = find2(e[i].u), y = find2(e[i].v); 77 if(x == y) { 78 return false; 79 } else { 80 f[y] = x; 81 } 82 } 83 } 84 return true; 85 }*/ 86 87 void cal(int k, int lim, int num, int T) 88 { 89 if(k == lim + 1) { 90 // cout<<num<<endl; 91 if(num == T) { 92 sum++; 93 } 94 return; 95 } 96 cal(k + 1, lim, num, T); 97 int x = find(e[k].u), y = find(e[k].v); 98 if(x != y) { 99 father[y] = x; 100 cal(k + 1, lim, num + 1, T); 101 father[x] = x, father[y] = y; 102 } 103 } 104 int main() 105 { 106 //freopen("award9.in", "r", stdin); 107 //freopen("t.out", "w", stdout); 108 n = read(), m = read(); 109 init(); 110 for(int i = 1; i <= m; i++) { 111 e[i].u = read(), e[i].v = read(), e[i].w = read(); 112 } 113 sort(e + 1, e + m + 1, cmp); 114 cnt = 0; 115 for(int i = 1; i <= m; i++) { 116 int x = find(e[i].u), y = find(e[i].v); 117 if(x != y) { 118 T++; 119 father[x] = y; 120 sum++; 121 } 122 if(e[i].w != e[i + 1].w) { 123 wait[++cnt].l = last; 124 wait[cnt].r = i; 125 wait[cnt].num = T; 126 T = 0; 127 last = i + 1; 128 } 129 } 130 if(sum < n - 1) { 131 printf("0\n"); 132 return 0; 133 } 134 init(); 135 for(int i = 1; i <= cnt; i++) { 136 sum = 0; 137 cal(wait[i].l, wait[i].r, 0, wait[i].num); 138 // cout<<wait[i].l<<" "<<wait[i].r<<" "<<wait[i].num<<endl; 139 // cout<<sum<<endl<<endl; 140 for(int j = wait[i].l; j <= wait[i].r; j++) { 141 int x = find(e[j].u), y = find(e[j].v); 142 if(x != y) { 143 father[y] = x; 144 } 145 } 146 ans = ans * sum % mod; 147 } 148 printf("%lld\n", ans); 149 }