1016: [JSOI2008]最小生成树计数
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算法是个好东西。
在kruskal算法中,我们总是尽可能地把权值较小的边加入最小生成树,这样得到的最小生成树一定是最优的。
所以对于每一种权值的边,我们所选择的加入最小生成树的边的数量是确定的(如果多选择一条边,则这条边一定是多余的;如果少选择一条边,那么就会有一条权值更大的边来代替这条边,显然这样构造出来的就不是最小生成树了)。
因此,我们对每一种权值的边单独考虑。
注意到同一种权值的边最多有10条,满足搜索复杂度。
首先计算出这种权值的边我们需要选择多少条,然后dfs判断加入哪些边,统计方案数即可。
最后运用乘法原理计算出总方案数。
注意,这里的并查集不能用路径压缩!因为dfs回溯的时候需要把之前加入的边删去。
我的代码有几个地方可能写繁了,不过数据范围很和谐,8ms跑过丝毫不怂。
别忘了特判图本身不连通的情况!!!
/************************************************************** Problem: 1016 User: xialan Language: C++ Result: Accepted Time:8 ms Memory:1308 kb ****************************************************************/ #include<iostream> #include<cstdio> #include<climits> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define rep(i,a,b) for(int i=a;i<=b;i++) #define dep(i,a,b) for(int i=a;i>=b;i--) inline int read(){ int x=0;char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x; } const int M=1001,MOD=31011; int fa[M],sum,j,MAX; struct edge{int l,r,v;}e[M]; bool cmp(edge x,edge y){return x.v<y.v;} int Find(int x){return fa[x]==x?x:Find(fa[x]);} void dfs(int x,int s){ if(x==j+1){ if(s>MAX)MAX=s,sum=1; else if(s==MAX)sum++; return; } int fx=Find(e[x].l),fy=Find(e[x].r); if(fx!=fy){ fa[fy]=fx; dfs(x+1,s+1); fa[fy]=fy; } dfs(x+1,s); } int main(){ int n=read(),m=read(); rep(i,1,n)fa[i]=i; rep(i,1,m){e[i].l=read();e[i].r=read();e[i].v=read();} sort(e+1,e+m+1,cmp); int ans=1,i=1; while(i<=m){ j=i; while(j<m&&e[j+1].v==e[j].v)j++; MAX=-1; dfs(i,0); ans=ans*sum%MOD; rep(k,i,j){ int fx=Find(e[k].l),fy=Find(e[k].r); if(fx!=fy)fa[fy]=fx; } i=j+1; } bool flag=1;int f=Find(1); rep(i,2,n)if(Find(i)!=f){flag=0;break;} if(flag)printf("%d\n",ans);else printf("0\n"); return 0; }