洛谷 #4208. 最小生成树计数

4 篇文章 0 订阅
4 篇文章 0 订阅

题意

求图中最小生成树的个数

题解

先求出最小生成树(Kruskal)统计不同长度边的数量,若无法构成生成树输出0
然后dfs遍历每一条边,两个端点连或不连,若最终边的数量与开始统计的相等,则这种长度边的取法+1
最后乘法原理得出ans

调试记录

p u t s ( 0 ) 摆 在 那 边 , 调 了 1 h + / ( ㄒ o ㄒ ) /    puts(0)摆在那边,调了1h+/(ㄒoㄒ)/~~ puts(0)1h+/(o)/  
#include <cstdio>
#include <algorithm>
#define maxn 1005
#define mo 31011
//#define int long long

using namespace std;

struct node{
	int u, v, l;
}e[maxn];

struct node2{
	int l, r, num;
}same[maxn];

int n, m, f[maxn];

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

bool cmp(node a, node b){ return a.l < b.l; }

int cnt_same = 0, cnt_e = 0;
void Kruskal(){
	sort(e + 1, e + m + 1, cmp);
	for (int i = 1; i <= n; i++) f[i] = i;
	
	for (int i = 1; i <= m; i++){
		if (e[i - 1].l != e[i].l) same[cnt_same].r = i - 1, same[++cnt_same].l = i;
		int fx = getf(e[i].u), fy = getf(e[i].v);
		
		if (fx != fy){
			f[fx] = fy;
			cnt_e++;
			same[cnt_same].num++;
		}
	}
	
	same[cnt_same].r = m;
}

int sum_dfs;
void dfs(int id_same, int cur, int num){
	if (cur == same[id_same].r + 1){
		if (num == same[id_same].num) (sum_dfs += 1) %= mo;
		return;
	}
	
	int fx = getf(e[cur].u), fy = getf(e[cur].v);
	
	if (fx != fy){
		f[fx] = fy;
		dfs(id_same, cur + 1, num + 1);
		f[fx] = fx, f[fy] = fy;
	}
	dfs(id_same, cur + 1, num);
}

int ans;

int main(){
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++) 
		scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].l);
	
	Kruskal();
	
	if (cnt_e != n - 1){ puts("0"); return 0; }
	
	for (int i = 1; i <= n; i++) f[i] = i;
	ans = 1;
	for (int i = 1; i <= cnt_same; i++){
		sum_dfs = 0;
		dfs(i, same[i].l, 0);
		(ans *= sum_dfs) %= mo;
		for (int j = same[i].l; j <= same[i].r; j++){
			int fx = getf(e[j].u), fy = getf(e[j].v);
			
			if (fx != fy) f[fx] = fy;
		}
	}
	printf("%d\n", ans);
	
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值