BZOJ 1016: [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 6357 Solved: 2575
[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,000。数据保证不会出现自回边和重边。注意:具有相同权值的边不会超过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
Sample Output
8
题解
性质:一个无向图所有的最小生成树中某种权值的边的数目均相同。
证明:(by wyfcyx_forever)
开始时,每个点单独构成一个集合。
首先只考虑权值最小的边,将它们全部添加进图中,并去掉环,由于是全部尝试添加,那么只要是用这种权值的边能够连通的点,最终就一定能在一个集合中。
那么不管添加的是哪些边,最终形成的集合数都是一定的,且集合的划分情况一定相同。那么真正添加的边数也是相同的。因为每添加一条边集合的数目便减少1.
那么权值第二小的边呢?我们将之间得到的集合每个集合都缩为一个点,那么权值第二小的边就变成了当前权值最小的边,也有上述的结论。
因此每个阶段,添加的边数都是相同的。我们以权值划分阶段,那么也就意味着某种权值的边的数目是完全相同的。
先跑出一个最小生成树,map[i]记录权值为i的边的数目。
从小到大处理每一种权值的边,状压枚举所有这种权值的边,看这种权值的边出现map[i]次时能否全部加入当前的森林。若能,则这种权值的边加入的方案数+1。
最终答案是每种权值的边能加入的方案数的乘积。
代码
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<map> using namespace std; const int N=105,M=1005,C=1000000005,mod=31011; int n,m,tot,t,ans=1; int fa[N],temp[N]; map<int,int>cnt; struct edge{ int u,v,w; }e[M]; bool cmp(edge a,edge b){ return a.w<b.w; } int find(int u){ return fa[u]==u?u:fa[u]=find(fa[u]); } int count(int x){ int ret=0; while(x){ if(x&1)ret++; x>>=1; } return ret; } int main(){ scanf("%d%d",&n,&m); int u,v,w; for(int i=1;i<=m;i++){ scanf("%d%d%d",&u,&v,&w); e[i]=(edge){u,v,w}; } sort(e+1,e+m+1,cmp); for(int i=1;i<=n;i++)fa[i]=i; int fu,fv; for(int i=1;i<=m;i++){ u=e[i].u,v=e[i].v,w=e[i].w; fu=find(u),fv=find(v); if(fu!=fv){ fa[fu]=fv; cnt[w]++; tot++; } if(tot==n-1)break; } if(tot<n-1){ printf("0\n"); return 0; } int l,r,fg; for(int i=1;i<=n;i++)fa[i]=i; for(l=1;l<=m;l=r+1){ memcpy(temp,fa,sizeof(temp)); t=0; w=e[l].w; r=l; while(e[r+1].w==w)r++; for(int i=0;i<(1<<(r-l+1));i++){ fg=1; if(count(i)!=cnt[w])continue; memcpy(fa,temp,sizeof(temp)); for(int j=l;j<=r;j++){ if((i>>(j-l))&1){ u=e[j].u,v=e[j].v; fu=find(u),fv=find(v); if(fu==fv){ fg=0; break; } fa[fu]=fv; } } if(fg)t++; } ans=(ans*t)%mod; memcpy(fa,temp,sizeof(temp)); for(int i=l;i<=r;i++){ u=e[i].u,v=e[i].v; fu=find(u),fv=find(v); if(fu!=fv)fa[fu]=fv; } } printf("%d\n",ans); return 0; }