CF156D-Prufer序列、多項式定理

yoshinow2001發表於2024-04-09

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;
}

相關文章