BZOJ 1016 最小生成树计数

http://www.lydsy.com/JudgeOnline/problem.php?id=1016

思路:

有这样一个性质:同一个图中最小生成树的权值相同的边数量相同。

我们来证明一下:假如一开始全部初始化,i的并查集父亲为i,那么假如最小权值的边没有构成环,那么这些边全部选入。

假如构成了环,那么必须会砍掉一些边,但不论怎么砍,这时构成的联通集合是一样的,而且砍掉的数量也一定一样。

那么怎么拓展到图中已经有一些边的情况呢?很简单,就是讲已经连起来的点缩成一个点,这样就和一开始的情况一样了,这样此时的边可能出现"自环",不过由于是最小生成树,这条边的权值一定大于等于环上的任意一条边,所以这条边不会被选入。

这样就简单了,我们用并查集维护,对相同权值的边的每个联通块,我们都做矩阵树定理,利用乘法原理,一个一个乘起来。

  1 #include<algorithm>
  2 #include<cstdio>
  3 #include<cmath>
  4 #include<cstring>
  5 #include<iostream>
  6 #include<vector>
  7 const int Mod=31011;
  8 const double eps=1e-6;
  9 std::vector<int>v[115];
 10 struct edge{
 11     int u,v,w;
 12 }e[200005];
 13 int fa[115],Fa[115],n,m,vis[115];
 14 int c[115][115],g[115][115];
 15 int read(){
 16     char ch=getchar();int t=0,f=1;
 17     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
 18     while ('0'<=ch&&ch<='9'){t=t*10+ch-'0';ch=getchar();}
 19     return t*f;
 20 }
 21 bool cmp(edge a,edge b){
 22     return a.w<b.w;
 23 }
 24 int find(int x,int f[]){
 25     if (f[x]==x) return x;
 26     else return find(f[x],f);
 27 }
 28 int sgn(double x){
 29     if (x<-eps) return -1;
 30     if (x>eps) return 1;
 31     return 0;
 32 }
 33 int gauss(int a[][115],int n){
 34     int res=1;
 35     for (int i=1;i<=n;i++)
 36      for (int j=1;j<=n;j++)
 37       a[i][j]%=Mod;
 38     for (int i=1;i<=n;i++){
 39         for (int j=i+1;j<=n;j++)
 40          while (a[j][i]){
 41                 int t=a[i][i]/a[j][i];
 42                 for (int k=i;k<=n;k++)
 43                  a[i][k]=(a[i][k]-t*a[j][k])%Mod;
 44                 for (int k=i;k<=n;k++)
 45                  std::swap(a[i][k],a[j][k]);  
 46                 res=-res;  
 47          }
 48         if (a[i][i]==0) return 0;
 49         res=(res*a[i][i])%Mod;
 50     }
 51     if (res<0) res=-res;
 52     res=(res+Mod)%Mod;
 53     return res; 
 54 }
 55 int main(){
 56     n=read();m=read();
 57     memset(g,0,sizeof g);
 58     for (int i=1;i<=n;i++) 
 59      v[i].clear();
 60     for (int i=1;i<=m;i++){
 61         e[i].u=read(),e[i].v=read(),e[i].w=read();
 62     }
 63     int ans=1;
 64     for (int i=1;i<=n;i++) fa[i]=i,vis[i]=0;
 65     std::sort(e+1,e+1+m,cmp);
 66     e[0].w=e[1].w;
 67     int Edge=-1;
 68     for (int i=1;i<=m+1;i++){
 69         if (i==m+1||e[i].w!=Edge){
 70             for (int j=1;j<=n;j++)
 71              if (vis[j])
 72               v[find(j,Fa)].push_back(j),vis[j]=0;
 73             for (int j=1;j<=n;j++)
 74              if (v[j].size()>1){
 75               for (int k=1;k<=n;k++)
 76                for (int l=1;l<=n;l++)
 77                 c[k][l]=0;
 78               int len=v[j].size();
 79               for (int a=0;a<len;a++)
 80                for (int b=a+1;b<len;b++){
 81                     int a1=v[j][a],b1=v[j][b];
 82                     c[a+1][b+1]=(c[b+1][a+1]-=g[a1][b1]);
 83                     c[a+1][a+1]+=g[a1][b1];
 84                     c[b+1][b+1]+=g[a1][b1];
 85                }  
 86               int res=(int)gauss(c,len-1);
 87               ans=(ans*res)%Mod;
 88               for (int a=0;a<len;a++)
 89                 fa[v[j][a]]=j; 
 90              }  
 91             for (int j=1;j<=n;j++){
 92                 v[j].clear();
 93                 Fa[j]=fa[j]=find(j,fa);
 94             } 
 95             if (i==m+1) break;
 96             Edge=e[i].w;
 97         }
 98         int u=e[i].u,v=e[i].v;
 99         int u1=find(u,fa),v1=find(v,fa);
100         if (u1==v1) continue;
101         vis[u1]=vis[v1]=1;
102         Fa[find(u1,Fa)]=find(v1,Fa);
103         g[u1][v1]++;
104         g[v1][u1]++;
105     }
106     int U=find(1,fa);
107     for (int i=2;i<=n;i++)
108      if (U!=find(i,fa)) {
109             puts("0");
110             return 0;
111     }
112     if (m<n-1){
113         puts("0");
114         return 0;
115     }
116     printf("%d\n",ans); 
117 }

 

posted @ 2016-06-14 08:28  GFY  阅读(...)  评论(... 编辑 收藏