BZOJ1016 JSOI2008 最小生成树计数

首先必须知道这样一个性质:同一个图的所有最小生成树等权值的边的数量相等、

那么我们先求任意的一个MST、得到每个权值出现的次数(这里可以先离散化方便处理)、

然后根据题目所给的很好的性质(每个权值出现不超过10次)、对每个权值用2^10枚举取边的情况、然后再看是否还存在一棵MST、

复杂度大概是不到2^10*M*M/10的、、

 

Code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <ctime>

#define ps system("pause")
#define message printf("*\n")
#define pb push_back
#define X first
#define Y second

using namespace std;

struct node{
	int a,b,w;
}edge[2010];

int f[110],use[10010],powe[20];
int cnt,n,m,minnum,ans,cur,res;

int find(int x){
	if	(f[x]==x)	return	x;
	f[x]=find(f[x]);
	return	f[x];
}

bool cmp(node aa,node bb){
	return	aa.w<bb.w;
}

void setup(){
	int cur=1;
	for	(int i=1;i<=m;i++)
		if	(edge[i].w!=edge[i+1].w)	edge[i].w=cur++;
		else	edge[i].w=cur;
}

int calc(int x){
	int v=0;
	while	(x){
		v+=x&1;
		x/=2;
	}
	return	v;
}

bool check(int x,int sit){
	cnt=n;res=edge[x].w*use[edge[x].w];
	for	(int i=1;i<=n;i++)	f[i]=i;
	for	(int i=x;edge[i].w==edge[x].w;i++){
		if	(sit&1){
			f[edge[i].a]=find(edge[i].a);
			f[edge[i].b]=find(edge[i].b);
			if	(f[edge[i].a]!=f[edge[i].b]){
				f[f[edge[i].b]]=f[edge[i].a];
				--cnt;
			}
		}
		sit/=2;
	}
	for	(int i=1;i<=m && cnt>1;i++){
		if	(edge[i].w==edge[x].w)	continue;
		f[edge[i].a]=find(edge[i].a);
		f[edge[i].b]=find(edge[i].b);
		if	(f[edge[i].a]==f[edge[i].b])	continue;
		f[f[edge[i].b]]=f[edge[i].a];
		res+=edge[i].w;
		if	(--cnt==1)	break;
	}
	return	(cnt==1 && res==minnum);
}

int main(){
	scanf("%d%d",&n,&m);
	for	(int i=1;i<=m;i++)
		scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].w);
	sort(edge+1,edge+m+1,cmp);
	setup();
	cnt=n;minnum=0;
	for	(int i=1;i<=n;i++)	f[i]=i;
	for	(int i=1;i<=m && cnt>1;i++){
		f[edge[i].a]=find(edge[i].a);
		f[edge[i].b]=find(edge[i].b);
		if	(f[edge[i].a]==f[edge[i].b])	continue;
		f[f[edge[i].b]]=f[edge[i].a];
		use[edge[i].w]++;
		minnum+=edge[i].w;
		if	(--cnt==1)	break;
	}
	if	(cnt!=1){
		printf("0\n");
		return	0;
	}
	ans=1;powe[0]=1;
	for	(int i=1;i<=10;i++)	powe[i]=powe[i-1]*2;
	for	(int i=1;i<=m;){
		int j=i+1;
		while	(edge[i].w==edge[j].w)	j++;
		if	(use[edge[i].w]){
			cur=0;
			for	(int sit=0;sit<powe[j-i];sit++)
				if	(calc(sit)==use[edge[i].w] && check(i,sit))	cur++;
			ans=ans*cur%31011;
		}
		i=j;
	}
	printf("%d\n",ans);
	return	0;
}

  

你可能感兴趣的:(最小生成树)