Description
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对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求一次最小生成树,同时记录每种权值的边使用次数,由于对每棵最小生成树,其中每种权值的边的数量是相等的,所以再使用dfs+并查集计算每种边的使用方案,再用乘法原理即可,下面是程序:
#include<stdio.h> #include<vector> #include<algorithm> #include<iostream> using namespace std; const int mod=31011,N=105,M=1005; struct edge{ int u,v,w; bool operator <(const edge p)const{ return w<p.w; } }a[M]; vector<int>w[M]; int f[N],head[N],cnt[M]; void read(int &s){ s=0; char c=getchar(); while(c<'0'||c>'9'){ c=getchar(); } while(c>='0'&&c<='9'){ s*=10; s+=c-'0'; c=getchar(); } } int findf1(int u){ return f[u]=u==f[u]?u:findf1(f[u]); } int findf2(int u){ while(u!=f[u]){ u=f[u]; } return u; } int dfs(int i,int k,int s){ if(i==w[k].size()){ return s==cnt[k]; } int tp=0; if(s<cnt[k]){ int x=findf2(a[w[k][i]].u),y=findf2(a[w[k][i]].v); if(x!=y){ f[x]=y; tp+=dfs(i+1,k,s+1); f[x]=x; } } if(s+w[k].size()-i-1>=cnt[k]){ tp+=dfs(i+1,k,s); } return tp; } int main(){ int n,m,i,j,s=1,k=0,sum=0; read(n); read(m); for(i=1;i<=m;i++){ read(a[i].u); read(a[i].v); read(a[i].w); } sort(a+1,a+m+1); for(i=1;i<=n;i++){ f[i]=i; } for(i=1;i<=m;i++){ if(a[i].w!=a[i-1].w){ k++; } w[k].push_back(i); int x=findf1(a[i].u),y=findf1(a[i].v); if(x!=y){ f[x]=y; cnt[k]++; sum++; } } if(sum<n-1){ printf("0\n"); return 0; } for(i=1;i<=n;i++){ f[i]=i; } for(i=1;i<=k;i++){ s*=dfs(0,i,0); s%=mod; for(j=0;j<w[i].size();j++){ int x=findf1(a[w[i][j]].u),y=findf1(a[w[i][j]].v); if(x!=y){ f[x]=y; } } } printf("%d\n",s); return 0; }