QQ联系我

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;
}
posted @ 2017-10-14 11:34  czy020202  阅读(...)  评论(... 编辑 收藏