[关键字]:数学 树结构
[题目大意]:求出给定的图的最小生成树的数量。
//==============================================================================================
[分析]:有一个定理:所有最小生成树的权值为ci的边的数量ni和所连接的点集S是一样。由此可以判断每种权值的边拿出ni个构成S的最小生成树的数量,因为同样的边的数量最多只有10所以搜索即可,然后利用乘法原理成在一起。注意判断无解情况。
[代码]:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAXM=1005; const int MAXN=105; const int MOD=31011; struct node { int x,y,d; }e[MAXM]; int n,m,size,tot; int sum[MAXM],st[MAXM],f[MAXN],f2[MAXN]; bool v[MAXN]; long long temp,ans=1; bool cmp(node a,node b){return a.d<b.d;} int GET(int k) { if (f[k]==k) return k; f[k]=GET(f[k]); return f[k]; } int GET2(int k) { /*if (f2[k]==k) return k; f2[k]=GET(f2[k]); return f2[k];*/ if (f2[k]==k) return k; else return GET2(f2[k]); } void Union(int x,int y) { int xx=GET(x),yy=GET(y); if (xx!=yy) f[xx]=yy; } void DFS(int st,int ed,int sum) { if (sum==0) {++temp;return;} if (st>ed) return; int fa=GET2(e[st].x),fb=GET2(e[st].y); if (v[e[st].x] && v[e[st].y] && fa!=fb) { f2[fa]=fb; DFS(st+1,ed,sum-1); f2[fa]=fa; } DFS(st+1,ed,sum); } void Work(int x) { temp=0; DFS(st[x],st[x+1]-1,sum[x]); ans=(ans*temp)%MOD; } bool Kurscal() { int i,j; sort(e+1,e+m+1,cmp); for (int i=1;i<=n;++i) f[i]=f2[i]=i; tot=size=0; memset(v,0,sizeof(v)); for (i=1;i<=m;++i) { if (i==1 || e[i].d!=e[i-1].d) { st[++tot]=i,sum[tot]=0; if (i!=1) Work(tot-1); for (j=1;j<=n;++j) f2[j]=f[j]; } if (GET(e[i].x)!=GET(e[i].y)) { ++size; Union(e[i].x,e[i].y); v[e[i].x]=v[e[i].y]=1; ++sum[tot]; } if (size==n-1) break; } for (j=i+1;j<=m+1;++j) if (e[j].d!=e[j-1].d) {st[tot+1]=j;break;} Work(tot); if (size==n-1) return 1; else return 0; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;++i) scanf("%d %d %d",&e[i].x,&e[i].y,&e[i].d); if (Kurscal()) printf("%I64d\n",ans); else printf("0\n"); return 0; }