1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 7429 Solved: 3098
[Submit][Status][Discuss]
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
题目链接:
http://www.lydsy.com/JudgeOnline/problem.php?id=1016
Solution
首先可以发现在不同的最小生成树中,同权值的边的数量是一样的。。
于是可以将每种权值的边的贡献分开算,然后用乘法原理就可以了。。
对于某一种颜色,直接爆搜。。。感觉复杂度好像不太对。。。但是过了。。。
算了反正都不知道多久之前写的了。。。
代码
#include<iostream> #include<cstdio> #include<algorithm> #define M 1010 #define mod 31011 using namespace std; int n,m,cnt,tot; int f[M],w[M],sum[M]; bool c[M]; struct edge{ int l,r,w; }e[M],a[M]; bool cmp(edge p,edge q){return p.w<q.w;} int find(int x){ if(f[x]==x) return f[x]; return find(f[x]); } void RE(){for(int i=1;i<=n;i++) f[i]=i;} void dfs(int x,int now,int k){ if(now==a[x].r+1){ if(k==a[x].w)tot++; return; } int xx=find(e[now].l),yy=find(e[now].r); if(xx!=yy){ f[xx]=yy; dfs(x,now+1,k+1); f[xx]=xx;f[yy]=yy; } dfs(x,now+1,k); } int main(){ int ans=1; cnt=0;tot=0; int xx,yy; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].w); sort(e+1,e+1+m,cmp); RE(); for(int i=1;i<=m;i++){ if(e[i].w!=e[i-1].w){a[cnt].r=i-1;cnt++;a[cnt].l=i;} xx=find(e[i].l);yy=find(e[i].r); if(xx!=yy){f[xx]=yy;a[cnt].w++;tot++;} } a[cnt].r=m; if(tot+1!=n){printf("0 ");return 0;} RE(); for(int i=1;i<=cnt;i++){ tot=0; dfs(i,a[i].l,0); ans=(ans*tot)%mod; for(int j=a[i].l;j<=a[i].r;j++){ xx=find(e[j].l);yy=find(e[j].r); if(xx!=yy) f[xx]=yy; } } printf("%d ",ans); return 0; }
This passage is made by Iscream-2001.