#Prufer序列,二项式定理,并查集#CF156D Clues
题目
给定一个 \(n\) 个点 \(m\) 条边的带标号无向图,它有 \(k\) 个连通块,求添加 \(k−1\) 条边使得整个图连通的方案数,答案对 \(p\) 取模。
分析
先用并查集把连通块的个数和每个连通块的大小求出来,添加 \(k-1\) 条边相当于总度数添加 \(2k-2\),
而每个连通块至少需要一个度数,那么相当于对剩下的度数分配形成 Prufer 序列,
也就是 \(\frac{(k-2)!}{\prod (d_i-1)}\),而往外连边就是 \({siz_i}^{d_i}\),根据广义二项式定理可以化简为
\[\sum \frac{(k-2)!}{\prod (d_i-1)}\prod {siz_i}^{d_i}=\prod {siz_i}(\sum siz_i)^{k-2}=n^{k-2}\prod{siz_i}
\]
当连通块个数为 \(1\) 时要特判
代码
#include <iostream>
using namespace std;
const int N=100011; int n,m,mod,f[N],siz[N],ans=1,block;
int getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
int ksm(int x,int y){
int ans=1;
for (;y;y>>=1,x=1ll*x*x%mod)
if (y&1) ans=1ll*ans*x%mod;
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m>>mod;
for (int i=1;i<=n;++i) f[i]=i,siz[i]=1;
for (int i=1;i<=m;++i){
int x,y; cin>>x>>y;
if (getf(x)!=getf(y)){
int fa=getf(x),fb=getf(y);
if (fa<fb) fa^=fb,fb^=fa,fa^=fb;
f[fa]=fb,siz[fb]+=siz[fa];
}
}
for (int i=1;i<=n;++i)
if (getf(i)==i) ans=1ll*ans*siz[i]%mod,++block;
if (block==1) cout<<1%mod<<'\n';
else cout<<1ll*ans*ksm(n,block-2)%mod<<'\n';
return 0;
}

浙公网安备 33010602011771号