BZOJ1016 [JSOI2008]最小生成树计数

题目意思是给出一无向图,求这个图的最小生成树的个数。

这里需要知道两个关于图的最小生成树的性质吧。

1.一个图的最小生成树中的某一权值的边的个数是确定的。

2.这些边的作用是确定的。

简单的证明:

最开始的时候初始化所有节点自己为一个集合,然后首先把最小的权值的边全都加入进去,这时候应该会出现环,我们去掉几条边,使得现在的图中没有环,这样对于最小的权值的边来说,所用到的边数是确定的,并且他们所形成的联通分量是一样的。

当把权值为第二小的边加进去的时候,把前面形成的联通分量看成是一个点,那么就和上面一样了。

 

这个题的做法,对于每一个权值的边集来说计算出用他们形成生成树时能用到的边组的方案数(如果用的话),最后所有的权值的方案书相乘就是最小生成树的个数。

 1 #include <cstdio>
 2 #include <vector>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 const int maxn = 1005;
 7 int fa[105],vis[maxn];
 8 const int mod = 31011;
 9 struct node{
10     int u,v,w,ans;
11 }a[maxn];
12 vector<int>w[maxn];
13 int x[maxn],p[maxn],xx[maxn];
14 int findf(int x){
15     int rt = x;
16     while(rt!=fa[rt])rt = fa[rt];
17     while(rt!=x){
18         int t = fa[x];
19         fa[x] = rt;
20         x = t;
21     }
22     return rt;
23 }
24 int findd(int x){
25     int rt = x;
26     while(rt!=p[rt])rt = p[rt];
27     while(rt!=x){
28         int t = p[x];
29         p[x] = rt;
30         x = t;
31     }
32     return rt;
33 }
34 bool cmp(node a,node b){
35     return a.w<b.w;
36 }
37 int main()
38 {
39     int n,m;scanf("%d%d",&n,&m);
40     for(int i = 1;i<=m;++i)scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
41     sort(a+1,a+1+m,cmp);
42     int cnt = 0;
43     for(int i = 1;i<=m;++i)
44     {
45         if(a[i].w==a[i-1].w)a[i].ans = a[i-1].ans;
46         else a[i].ans = ++cnt;
47         w[a[i].ans].push_back(i);
48     }
49     int ss = 0;
50     for(int i = 1;i<=n;++i)fa[i] = i;
51     for(int i = 1;i<=m;++i){
52         int fx = findf(a[i].u),fy = findf(a[i].v);
53         if(fx==fy)continue;
54         fa[fx] = fy;
55         xx[a[i].ans]++;
56         ss++;
57     }
58     if(ss!=n-1){printf("0\n");return 0;}
59     for(int i = 0;i<=11;++i)x[i] = 1<<i;
60     for(int i = 1;i<=n;++i)fa[i] = i;
61     int ans = 1;
62     for(int i = 1;i<=cnt;++i){
63         int len = 1<<w[i].size(),sum = 0,kase = 0;
64         for(int j = 1;j<len;++j){
65             int ok = 1,tot = 0;
66             for(int k = 1;k<=n;++k)p[k] = fa[k];
67             for(int k = 0;k<w[i].size();++k)if(j&x[k]){
68                 int fx = findd(a[w[i][k]].u),fy = findd(a[w[i][k]].v);
69                 if(fx==fy){ok = 0;break;}
70                 p[fx] = fy;
71                 tot++;
72             }
73             if(ok&&tot==xx[i])kase = j,sum++;
74         }
75         if(sum)ans = (ans*sum)%mod;
76         if(kase)for(int k = 0;k<w[i].size();++k)if(kase&x[k])
77             fa[findf(a[w[i][k]].u)] = findf(a[w[i][k]].v);
78     }
79     printf("%d\n",ans);
80 
81     return 0;
82 }

 

posted on 2015-08-02 23:23  round_0  阅读(185)  评论(0编辑  收藏  举报

导航