CF 156D Clues

题目描述

https://codeforces.com/problemset/problem/156/D

简要题意:给定一个 $n$ 个点 $m$ 条边的简单无向图,如果原图有 $k$ 个连通块,求添加 $k-1$ 条边是整个图连通的方案数

$n,m\le 10^5$

Solution

我们令 $d_i$ 表示第 $i$ 个连通块的度数,$s_i$ 表示第 $i$ 个连通块的大小,那么我们有

这个式子大概就是我们枚举每个连通块的度数,然后根据 $prufer$ 序列来计数,后面那个 $\prod$ 是因为每个连通块向外连边有 $s_i$ 种选择

这个式子我们利用多项式定理来可以得到 $ans=n^{k-2}\prod_{i=1}^ks_i$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <cstring>
#include <algorithm>
#define ll long long
#define maxn 100010
using namespace std;

int p;
ll pow_mod(ll x, ll n) {
ll s = 1;
for (; n; n >>= 1, x = x * x % p)
if (n & 1) s = s * x % p;
return s;
}

int n, m;

int fa[maxn], sz[maxn];
void init_fa(int n) { for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1; }
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline void merge(int x, int y) {
int fx = find(x), fy = find(y);
if (fx == fy) return ;
fa[fx] = fy; sz[fy] += sz[fx];
}

signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n >> m >> p; init_fa(n);
for (int i = 1; i <= m; ++i) {
int x, y; cin >> x >> y;
merge(x, y);
} int k = 0; ll t = 1;
for (int i = 1; i <= n; ++i) if (find(i) == i) ++k, t = t * sz[i] % p;
if (k == 1) cout << 1 % p << "\n";
else cout << pow_mod(n, k - 2) * t % p << "\n";
return 0;
}