BZOJ1543: 生成树计数【DFS+并查集】

博客围绕1543生成树计数展开,介绍了最小生成树的两个性质,即相同边权的出现次数和连通性相同。基于此,可通过DFS枚举相同边权的使用情况,再利用并查集撤销来完成生成树计数,速度很快。

1543: 生成树计数

首先我们知道最小生成树的两个性质。

1.相同边权的出现次数相同。

2.相同边权的连通性相同。

所以我们可以直接DFS枚举相同的边权用了哪些,然后并查集撤销就可以了。

快的飞起。

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN=50005,MAXM=100005,MOD=1000003;
int n,m,Ans=1,tot,Sum,V,fa[MAXN],P[MAXM],hsh[MAXM];
bool vis[MAXM];
struct xcw{
	int x,y,w;
	bool operator <(const xcw b)const{return w<b.w;}
}a[MAXM];
int get(int x){return x==fa[x]?x:fa[x]=get(fa[x]);}
void Kruskal(){
	tot=0;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){
		int fx=get(a[i].x),fy=get(a[i].y);
		if(fx==fy) continue;
		fa[fy]=fx;V+=a[i].w;tot++;hsh[P[i]]++;
	}
}
void Work(){
	tot=0;int Now=0;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
	if(vis[i]){
		int fx=get(a[i].x),fy=get(a[i].y);
		if(fx==fy) continue;
		fa[fy]=fx;Now+=a[i].w;tot++;
	}
	if(tot==n-1&&Now==V) Sum++;
}
void DFS(int Num,int Now,int R){
	if(Now>R){if(Num==0) Work();return;}
	vis[Now]=0,DFS(Num,Now+1,R);
	vis[Now]=1,DFS(Num-1,Now+1,R);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
	sort(a+1,a+1+m);
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++){P[i]=i;if(a[i].w==a[i-1].w) P[i]=P[i-1];}
	Kruskal();
	if(tot!=n-1){printf("0\n");return 0;}
	for(int i=1;i<=m;i++) vis[i]=1;
	for(int i=1,j;i<=m;i=j){
		j=i+1;
		while(P[i]==P[j]&&j<=m) j++;
		Sum=0;DFS(hsh[P[i]],i,j-1);if(Sum) Ans=(Ans*Sum)%MOD;
	}
	printf("%d\n",Ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值