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;
}

浙公网安备 33010602011771号