Codeforces Round #110 (Div. 1) D Clues

题目链接:Clues

题意:一个无向图,图中有很多联通块,现在要添加最少数目的边,使得整个图都联通,问有多少种加边的方法。

题解:对于联通块,直接并查集处理计数即可。并令联通块的数目为 t ,总点数为 n。

   剩下的内容需要先了解 prufer 序列:https://www.cnblogs.com/dirge/p/5503289.html

   因此可构造序列为 n^(t-2) 种。在构造 prufer 后还需要进行删块,由于一条边可能连进这个联通块内的任意点,所以对于删除的块 i,需要乘上 num[i]。最后剩下的两个块之间相互连边的数目也是块数相乘。所以最后的答案就是:nt-2×∏num[i]。需要特殊处理的时只有一个块时候的情况。

#include <bits/stdc++.h>
using namespace std;

int n,m,k;
int f[100005],num[100005];
int find(int x){
    return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx==fy) return;
    num[fx]+=num[fy];
    f[fy]=fx;
}
void init(){
    for(int i=1;i<=n;i++) f[i]=i,num[i]=1;
}

int vis[100005],block[100005],cnt;
int pow_mod(int a,int n){
    long long res=1,t=a;
    while(n){
        if(n&1) res=(res*t)%k;
        t=(t*t)%k;
        n/=2;
    }
    return res;
}
int solve(){
    if(cnt==1) return 1%k;

    long long res=pow_mod(n,cnt-2);
    for(int i=1;i<=cnt;i++){
        res=(res*block[i])%k;
    }
    return res;
}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    init();
    for(int i=1;i<=m;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        merge(u,v);
    }
    for(int i=1;i<=n;i++){
        int fx=find(i);
        if(vis[fx]) continue;
        vis[fx]=1;
        block[++cnt]=num[fx];
    }
    printf("%d\n",solve());

    return 0;
}

 

posted on 2019-01-17 12:29  Psong  阅读(224)  评论(0编辑  收藏  举报

导航