最小生成树计数 BZOJ 1016
最小生成树计数
【问题描述】
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生成树可能很多,所以你只需要输出方案数对31011的模就可以了。
【输入格式】
第一行包含两个数,n和m,其中1<=n<=100; 1<=m<=1000; 表示该无向图的节点数和边数。每个节点用1~n的整数编号。接下来的m行,每行包含两个整数:a, b, c,表示节点a, b之间的边的权值为c,其中1<=c<=1,000,000,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过10条。
【输出格式】
输出不同的最小生成树有多少个。你只需要输出数量对31011的模就可以了。
【样例输入】
4 6
1 2 1
1 3 1
1 4 1
2 3 2
2 4 1
3 4 1
【样例输出】
8
题解:
1.用克鲁斯卡尔求一遍最小生成树,统计出某一权值在最小生成树中的个数(即所有权值相同的边的个数)
2.考虑对于每一种权值的方案(Dfs)
要求:
(1)选出的边数等于最小生成树中该权值拥有的边数
原理:最小生成树中每种权值的边数不变
(2)不含环,判断是否有环可以用不压缩路径的并查集处理
原理:树中不含环
3.将所有权值的方案相乘
原理:在最小生成树中对于每一种权值的合法方案构建出的图的连通情况相同
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdlib>
5 #include<cstdio>
6 #include<cmath>
7 using namespace std;
8 inline int Get()
9 {
10 int x = 0, s = 1;
11 char c = getchar();
12 while('0' > c || c > '9')
13 {
14 if(c == '-') s = -1;
15 c = getchar();
16 }
17 while('0' <= c && c <= '9')
18 {
19 x = (x << 3) + (x << 1) + c - '0';
20 c = getchar();
21 }
22 return x * s;
23 }
24 struct shape
25 {
26 int x, y, z;
27 };
28 shape a[100233];
29 int n, m, k;
30 int tot;
31 int cnt;
32 int sum;
33 int l[100233];
34 int r[100233];
35 int s[100233];
36 int fat[100233];
37 int c[100233];
38 int u[100233];
39 inline bool rule(shape a, shape b)
40 {
41 return a.z < b.z;
42 }
43 int Find(int x)
44 {
45 while(fat[x] != x) x = fat[x];
46 return x;
47 }
48 void Dfs(int fig, int va, int le)
49 {
50 if(fig == s[va])
51 {
52 sum = (sum + 1) % 31011;
53 if(sum == 1)
54 for(int i = 0; i <= c[0]; ++i)
55 u[i] = c[i];
56 // for(int i = 1; i <= c[0]; ++i) printf("%d ", c[i]);
57 // cout<<endl;
58 return;
59 }
60 for(int i = le; i <= r[va]; ++i)
61 {
62 int x = Find(a[i].x);
63 int y = Find(a[i].y);
64 int cx = fat[x];
65 if(x == y) continue;
66 fat[x] = y;
67 c[++c[0]] = i;
68 Dfs(fig + 1, va, i + 1);
69 fat[x] = cx;
70 --c[0];
71 }
72 }
73 int main()
74 {
75 n = Get(), m = Get();
76 for(int i = 1; i <= m; ++i)
77 {
78 a[i].x = Get();
79 a[i].y = Get();
80 a[i].z = Get();
81 }
82 sort(a + 1, a + 1 + m, rule);
83 for(int i = 1; i <= n; ++i) fat[i] = i;
84 for(int i = 1; i <= m; ++i)
85 {
86 if(a[i].z != a[i - 1].z)
87 {
88 r[tot] = i - 1;
89 ++tot;
90 l[tot] = i;
91 }
92 if(k != n - 1)
93 {
94 int x = Find(a[i].x), y = Find(a[i].y);
95 if(x != y)
96 {
97 fat[x] = y;
98 ++s[tot];
99 ++k;
100 }
101 }
102 }
103 if(k != n - 1)
104 {
105 printf("0");
106 return 0;
107 }
108 r[tot] = m;
109 for(int i = 1; i <= n; ++i) fat[i] = i;
110 int ans = 1;
111 for(int i = 1; i <= tot; ++i)
112 if(s[i])
113 {
114 sum = 0;
115 Dfs(0, i, l[i]);
116 for(int l = 1; l <= u[0]; ++l)
117 {
118 int x = Find(a[u[l]].x);
119 int y = Find(a[u[l]].y);
120 fat[x] = y;
121 }
122 ans = (ans * sum) % 31011;
123 }
124 printf("%d", ans);
125 }