标签:nbsp ret 联通 round i++ ini pow pru 题目
题目链接: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; }
Codeforces Round #110 (Div. 1) D Clues
标签:nbsp ret 联通 round i++ ini pow pru 题目
原文地址:https://www.cnblogs.com/N-Psong/p/10281597.html