bzoj 1016: [JSOI2008]最小生成树计数

Description

  现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的
最小生成树。(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。由于不同的最小生
成树可能很多,所以你只需要输出方案数对31011的模就可以了。

solution

正解:kruskal+枚举
我们需要明白:对于所有的最小生成树满足:1.相同边权的边数相同。2.相同边权的边合并的点集也是一定的.
两个结论可以一起证明:边数显然不可能增加,不然在做第一次kruskal的时候肯定会被加入,如果边数还可以减少,那么有一些点肯定还没有被合并,那么合并可以使得边数+1.
所以我们枚举边权,因为相同边权的边不超过10,所以对每一种边权枚举,方案相乘即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=10005,mod=31011;
int fa[N],n,m,t=0;
struct node{
	int x,y,z;
	bool operator <(const node &pr)const{return z<pr.z;}
}e[N];
inline int find(int x){return fa[x]==x?x:find(fa[x]);}
int l[N],r[N],v[N];
bool priwork(){
	int x,y,cnt=0;
	for(int i=1;i<=m;i++){
		if(e[i].z!=e[i-1].z)t++,l[t]=i;
		r[t]=i;
		x=e[i].x;y=e[i].y;
		if(find(x)==find(y))continue;
		fa[find(y)]=find(x);
		cnt++;v[t]++;
	}
	if(cnt!=n-1)return false;
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
			if(find(i)!=find(j))return false;
	return true;
}
int cnt=0;
inline void dfs(int i,int x,int sum){
	if(x>r[i]){if(sum==v[i])cnt++;return ;}
	dfs(i,x+1,sum);
	int a=find(e[x].x),b=find(e[x].y);
	if(a!=b){
		fa[b]=a;
		dfs(i,x+1,sum+1);
		fa[b]=b;fa[a]=a;
	}
}
void work()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++)cin>>e[i].x>>e[i].y>>e[i].z;
   for(int i=1;i<=n;i++)fa[i]=i;
	sort(e+1,e+m+1);
	if(!priwork()){puts("0");return ;}
	int ans=1,x,y;
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=t;i++){
		cnt=0;
		dfs(i,l[i],0);
		ans*=cnt;if(ans>=mod)ans%=mod;
		for(int j=l[i];j<=r[i];j++){
			x=e[j].x;y=e[j].y;
			if(find(x)==find(y))continue;
			fa[find(y)]=find(x);
		}
	}
	cout<<ans<<endl;
}

int main()
{
	work();
	return 0;
}

posted @ 2017-12-14 21:41  PIPIBoss  阅读(148)  评论(0编辑  收藏  举报