CF156D Clues

题目链接:https://codeforces.com/problemset/problem/156/D
题目翻译:给定一个 n n n 个点 m m m 条边的带标号无向图,它有 k k k 个连通块,求添加 k − 1 k−1 k1 条边使得整个图连通的方案数,答案对 p p p 取模。

前置知识:prufer序列 n n n个有标号的点连成的无根树的方案数为: n n − 2 n ^ {n - 2} nn2

那么对于任意一个有 k k k个连通块的图,设每个连通块点数 s i s_i si, 连通块之间的连边等价于将每个连通块看成一个点 n n n个有标号的点连成无根树的方案数,即为 k k − 2 k ^ {k - 2} kk2

又因为每个连通块连边可以是连通块中任意一个点,所以每一种方案对应了原问题中 ∏ i = 1 k s i \prod_{i=1}^k s_i i=1ksi

所以总的方案数为: k k − 2 ∗ ∏ i = 1 k s i k ^ {k - 2} * \prod_{i = 1}^{k}s_i kk2i=1ksi
接下来就好算了

C o d e Code Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 1e5;
int f[MAXN + 10], cnt[MAXN + 10];
int get_fa(int);
inline int read();

signed main(){
	freopen ("std.in","r",stdin);
	freopen ("std.out","w",stdout);
	int n, m, p;
	n = read(), m =read(), p =read();
	for (register int i = 1; i <= n; ++i)	f[i] = i, cnt[i] = 1;
	for (register int i = 1; i <= m; ++i){
		int x = read(), y = read();
		x = get_fa(x), y = get_fa(y);
		if (x == y)	continue;
		f[y] = x;
		cnt[x] += cnt[y];
	}
	int sum = 0, ans = 1, num = 1;
	for (register int i = 1; i <= n; ++i){
		if (f[i] == i){
			++sum;
			ans = ans * cnt[i] % p;
		}
	}
	if (sum == 1){
		printf("%d\n", 1 % p);
		return 0;
	}
	for (register int i = 1; i <= sum - 2; ++i)	num = num * n % p;
	ans = ans * num % p;
	printf("%lld\n",ans);
	return 0;
}

inline int read(){
	int x = 0;
	char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = (x << 1) + (x << 3) + (c & 15), c = getchar();
	return x;
}

int get_fa(int x){
	if (f[x] == x)	return x;
	return f[x] = get_fa(f[x]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值