最小生成树计数--Kruskal+搜索

Loj 10070 Luogu 4208

在这里插入图片描述

题目分析:

  • 不同的最小生成树中相应权值边的个数是相等的
  • 先跑一遍 K r u s k a l Kruskal Kruskal,将相同权值的边放在一个块里,并且统计在最小生成树中有当前权值的边多少条
  • d f s dfs dfs统计答案,对于每一种权值的边,遍历每个块中的边,选或不选,若选入的边等于最小生成树中该权值边的条数, + + s u m ++sum ++sum,再根据乘法原理统计答案
  • 注意并查集不能路径压缩,不然回溯时回不去

Code

#include <bits/stdc++.h>
using namespace std;
#define mod 31011
#define maxn 110
#define maxm 1010

int cnt=0,n,m,sum=0,f[maxn],ans=1;
struct edge {
	int u,v,w;
}e[maxm];
struct node {
	int st,ed,w;
}ne[maxm];

inline void init_() {
	freopen("a.txt","r",stdin);
}

inline int read_() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	return x*f;
}

inline void clean_() {
	for(int i=1;i<=n;++i) f[i]=i;
	memset(ne,0,sizeof(ne));
}

//不能路径压缩 
int find_(int x) {
	if(f[x]==x) return x;
	else return find_(f[x]);
}

inline bool cmp_(edge aa,edge bb) {
	return aa.w<bb.w;
}

inline void kruskal_() {
	sort(e+1,e+m+1,cmp_);
	int tot=0;
	for(int i=1;i<=m;++i) {
		if(e[i].w!=e[i-1].w) {
			ne[++cnt].st=i;
			ne[cnt-1].ed=i-1;
		}
		int fx=find_(e[i].u),fy=find_(e[i].v);
		if(fx!=fy) {
			f[fx]=fy;
			++tot;
			++ne[cnt].w;
		}
	}
	ne[cnt].ed=m;
	if(tot<n-1) {
		printf("0");
		exit(0);
	}
}

void dfs_(int k,int x,int t) {
	if(x==ne[k].ed+1) {
		if(t==ne[k].w) ++sum;
		return;
	}
	int fx=find_(e[x].u),fy=find_(e[x].v);
	if(fx!=fy) {
		f[fx]=fy;
		dfs_(k,x+1,t+1);
		f[fx]=fx;f[fy]=fy;
	}
	dfs_(k,x+1,t);
}

void readda_() {
	n=read_();m=read_();
	clean_();
	for(int i=1;i<=m;++i) {
		e[i].u=read_();e[i].v=read_();e[i].w=read_();
	}
	kruskal_();
	for(int i=1;i<=n;++i) f[i]=i;
	for(int i=1;i<=cnt;++i) {       
	 	sum=0;
		dfs_(i,ne[i].st,0);
		ans=(ans*sum)%mod;
		for(int j=ne[i].st;j<=ne[i].ed;++j) {
			int fx=find_(e[j].u),fy=find_(e[j].v);
			if(fx!=fy) f[fx]=fy;
		}
	}
	printf("%d",ans%mod);
}

int main() {
//	init_();
	readda_();
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值