题目链接:https://codeforces.com/problemset/problem/156/D
题目翻译:给定一个
n
n
n 个点
m
m
m 条边的带标号无向图,它有
k
k
k 个连通块,求添加
k
−
1
k−1
k−1 条边使得整个图连通的方案数,答案对
p
p
p 取模。
前置知识:prufer序列, n n n个有标号的点连成的无根树的方案数为: n n − 2 n ^ {n - 2} nn−2
那么对于任意一个有 k k k个连通块的图,设每个连通块点数为 s i s_i si, 连通块之间的连边等价于将每个连通块看成一个点, n n n个有标号的点连成无根树的方案数,即为 k k − 2 k ^ {k - 2} kk−2
又因为每个连通块连边可以是连通块中任意一个点,所以每一种方案对应了原问题中 ∏ 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
kk−2∗i=1∏ksi
接下来就好算了
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]);
}