首页 > 代码库 > BZOJ 1016 JSOI2008 最小生成树计数 Kruskal

BZOJ 1016 JSOI2008 最小生成树计数 Kruskal

题目大意:给定一个无向图,求最小生成树的方案数

首先对于一个无向图的最小生成树,每种边权的边的数量是一定的

首先我们先跑一遍Kruskal,求出最小生成树上每种边权的出现次数

然后对于每种出现在最小生成树上的边权,我们从小到大处理

对于每种边权,我们枚举这种边权的边有多少种方案可以加进最小生成树上而不形成环 这个用状压处理

ans乘上这个值 然后把这种边权连接的所有联通块缩点

注意最小生成树不存在时输出0


#include<map>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 1010
#define Mo 31011
using namespace std;
struct edge{
	int x,y,f;
	bool operator < (const edge &z) const
	{
		return f < z.f ;
	}
}edges[M];
int n,m,ans=1,fa[110];
int digit[M+100];
map<int,int>a;
int Find(int x)
{
	if(!fa[x]||fa[x]==x)
		return fa[x]=x;
	return fa[x]=Find(fa[x]);
}
void Kruskal()
{
	int i,cnt=0;
	memset(fa,0,sizeof fa);
	for(i=1;i<=m;i++)
	{
		int x=Find(edges[i].x),y=Find(edges[i].y);
		if(x==y)
			continue;
		fa[x]=y;
		cnt++;a[edges[i].f]++;
	}
	if(cnt!=n-1)
	{
		puts("0");
		exit(0);
	}
}
int Deal(int x)
{
	int i,j,y,re=0,cnt=a[edges[x].f];
	for(y=x;edges[y].f==edges[y+1].f;y++);
	for(i=0;i<1<<y-x+1;i++)
		if(digit[i]==cnt)
		{
			int temp=i;
			memset(fa,0,sizeof fa);
			for(j=x;temp;temp>>=1,j++)
				if(temp&1)
				{
					int xx=Find(edges[j].x);
					int yy=Find(edges[j].y);
					if(xx==yy)
						break;
					fa[xx]=yy;
				}
			if(!temp)
				++re;
		}
	ans*=re,ans%=Mo;
	memset(fa,0,sizeof fa);
	for(i=x;i<=y;i++)
	{
		int xx=Find(edges[i].x),yy=Find(edges[i].y);
		if(xx==yy)
			continue;
		fa[xx]=yy;
	}
	for(i=y+1;i<=m;i++)
		edges[i].x=Find(edges[i].x),edges[i].y=Find(edges[i].y);
	return y;
}
int main()
{
	int i;
	for(i=0;i<1<<10;i++)
		digit[i]=digit[i>>1]+(i&1);
	cin>>n>>m;
	for(i=1;i<=m;i++)
		scanf("%d%d%d",&edges[i].x,&edges[i].y,&edges[i].f);
	sort(edges+1,edges+m+1);
	Kruskal();
	for(i=1;i<=m;i++)
		if(a[edges[i].f])
			i=Deal(i);
	cout<<ans<<endl;
	return 0;
}


BZOJ 1016 JSOI2008 最小生成树计数 Kruskal