【BZOJ1016】【JSOI2008】最小生成树计数 kruskal+dfs

题意:你认为我会告诉你题意么?!!!

题解:

    首先我们把边长从小到大排个序,这是显然的!

    然后我们把边长相同的分个组,难道你要问我为什么?

    然后我们在处理每个边组之前先存一下当前并查集状态,这是显然的!

    然后我们状态压缩枚举每种选边方案,看是否正确,难道你要问我为什么?

    只要边的两个节点所处集不同,然后加的边不会少个几条就是一种正确方案,这是显然的!

    这里就涉及了一个很好推的小性质:每个边组处理完了以后,无论是哪种方案,最后达成的并查集状态都是一定的,而且用的边数也都是一定的,啊?难道你要问我为什么?

    说完了,另外,要注意可能不存在最小生成树,我为了这个调了一下午,这是不科学的!


    好了,贴写得还可以的代码,另外,dfs处可以用状态压缩式写法,可能代码复杂度还会低点,而时间是一定小的。。。额,虽然题目中每个边组元素个数<=10的意思就是让你写暴力验证,但是还有一种Matrix Tree定理的高速做法!强烈点赞!快去看吧!

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 105
#define M 1005
#define K 11
#define mod 31011
#define inf 0x3f3f3f3f
using namespace std;

struct KSD
{
	int u,v,len;
	bool operator < (const KSD &a)const
	{return len<a.len;}
}road[M];
int f[3][N],n,m;
int find(int p,int x)
{
	if(f[p][x]==x)return x;
	return f[p][x]=find(p,f[p][x]);
}
int ans=1,ret,head,tail,edge;
bool visit[M];
void dfs(int dep)
{
	if(dep==tail)
	{
		int i,fa,fb,E=0;
		for(i=1;i<=n;i++)f[1][i]=f[0][i];
		for(i=head;i<tail;i++)if(visit[i])
		{
			fa=find(1,road[i].u);
			fb=find(1,road[i].v);
			if(fa==fb)return ;
			f[1][fb]=fa;
			E++;
		}
		if(E==edge)ret++;
		return ;
	}
	visit[dep]=0;dfs(dep+1);
	visit[dep]=1;dfs(dep+1);
}
bool Krus()
{
	int i,len,zsgg=0;
	int fa,fb;
	for(i=1;i<=n;i++)f[0][i]=f[2][i]=i;
	for(head=1;head<=m;head=tail)
	{
		len=road[head].len;
		tail=head-1;
		edge=0;
		while(road[++tail].len==len)
		{
			fa=find(2,road[tail].u);
			fb=find(2,road[tail].v);
			if(fa!=fb)f[2][fb]=fa,edge++,zsgg++;
		}
		ret=0;
		dfs(head);
		ans=ans*ret%mod;
		for(i=1;i<=n;i++)f[0][i]=find(2,i);
	}
	if(zsgg==n-1)return 1;
	return 0;
}


int main()
{
//	freopen("test.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)scanf("%d%d%d",&road[i].u,&road[i].v,&road[i].len);
	sort(road+1,road+m+1);
	printf("%d\n",Krus()?ans:0);
	return 0;
}


你可能感兴趣的:(最小生成树,暴搜,JSOI2008,最小生成树计数,BZOJ1016)