CF156D-Prufer序列、多项式定理
link:https://codeforces.com/contest/156/problem/D
题意:给一张无向简单图 \(G\),问有多少种加边的方式,使得图联通,并且需要加的边最小。
\(|E|,|V|\leq 10^5\),对 \(k\) 取模
前置知识应该是Prufer序列(这题应该是绕不开这个东西)
对每个连通分支考虑答案,如果有 \(k\) 个连通分支,大小分别为 \(s_1,\dots,s_k\),让他们联通意味着把连通块连成一棵树,假设每个连通分支在树上的度数是 \(d_i\) ,则其在 Prufer序列里出现 \(d_i-1\) 次,而连接连通分支的每条边可以在 \(s_i\) 个点中任意选择,因此共有
\[\sum_{d_1,\dots,d_k} \binom{k-2}{d_1-1,\dots,d_k-1} \prod_{i=1}^k s_i^{d_i} =(\prod_{i=1}^k s_i) \sum_{d_1,\dots,d_k}\binom{k-2}{d_1-1,\dots,d_k-1} \prod s_i^{d_i-1}
\]
根据多项式定理, $$(x_1+\dots+x_k)^m=\sum_{d_1,\dots,d_k}\binom{m}{d_1,\dots,d_k}\prod x_i^{d_i}$$
因此答案就是 \((\sum d_i-1)^{k-2}\prod_{i=1}^k s_i\),而 \(\sum s_i=n\),故答案是 \(n^{k-2}\prod_{i=1}^k s_i\),非常简洁
btw,需要小心模数为 \(1\) 的情况…
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,MOD,fa[N],sz[N];
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
int main(){
cin>>n>>m>>MOD;
rep(i,1,n)fa[i]=i,sz[i]=1;
rep(i,1,m){
int x,y;
cin>>x>>y;
int fx=find(x),fy=find(y);
if(fx==fy)continue;
fa[fy]=fx;
sz[fx]+=sz[fy];
}
int k=0,ret=1;
rep(i,1,n)if(find(i)==i){
ret=(ll)ret*sz[i]%MOD;
k++;
}
rep(i,1,k-2)ret=(ll)ret*n%MOD;
if(k==1)ret=1%MOD;
cout<<ret;
return 0;
}