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;
}
posted @ 2024-04-09 16:23  yoshinow2001  阅读(8)  评论(0编辑  收藏  举报