题面
给出一个大小为 $n$ 的无向图,求使得任意两点间只有1~2条简单路径的加边方案数。($n \leq 20$)。
题解
首先可以知道最终这个图要么是个树,要么是一个树多了一条边(也就是基环树)。
那么我就不会了。。。
后面了解到了 Prufer序列 这个神奇的东西,大致是一个可以 Serialize/Deserialize 一棵树的东西。利用这个东西呢,可以推出在 $n$ 个点的图中,使所有 $k$ 个联通块加 $k-1$ 条边联通的方案数恰好是$n^{k-2}\cdot\prod\limits_{i=1}^k siz_i$
然后呢,就比较好做了。
Case 1. 原本就有个环
数一下联通块,直接上Prufer联通求方案数即可。
Case 2. 原本没有环
首先加上没有环的方案数量(同 Case 1
)。
之后我们就要给它造环了。
我们可以造一个跨越一个联通块、两个联通块或多个联通块的环。
Case 2.1. 跨一个联通块
在联通块中任意连上一个之前没有的边。
Case 2.2. 跨两个联通块
在两个联通块之间连两条边(注意不要重边啦)
Case 2.3. 跨多个联通块
我们可以圆排列一下,然后在两个挨着的联通块之间连一条边。
最后把环理解成是一个联通块,然后按照 Case 1
处理即可。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1234567891,inv2=617283946;
inline ll qpow(ll x,int p){
ll ans=1;
while(p){
if(p&1)ans=ans*x%mod;
x=x*x%mod;
p>>=1;
}
return ans;
}
inline int popcount(int x){
int res=0;
while(x){x-=x&-x;res++;}
return res;
}
class TheCitiesAndRoadsDivOne{
int n;
int conn[22][22],fa[22],siz[22],blkcnt=0,blksiz[22];
ll fac[22];
inline int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
public:
int find(vector<string> mp){
n=mp.size();
fac[0]=1;
for(int i=1;i<=n;++i)fac[i]=fac[i-1]*i%mod;
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)conn[i+1][j+1]=(mp[i][j]=='Y');
for(int i=1;i<=n;++i)fa[i]=i,siz[i]=1;
bool ring=0;
for(int i=1;i<=n;++i){
for(int j=i+1;j<=n;++j){
if(!conn[i][j])continue;
int fai=findfa(i),faj=findfa(j);
if(fai==faj){
ring=1;
}else siz[fai]+=siz[faj];
fa[faj]=fai;
}
}
ll res1;
ll pisiz=1;
for(int i=1;i<=n;++i)if(findfa(i)==i)blksiz[++blkcnt]=siz[i],(pisiz*=siz[i])%=mod;
if(blkcnt==1)res1=1;
else{
res1=qpow(n,blkcnt-2)*pisiz%mod;
}
if(ring)return res1;
ll res2=0;
for(int i=1;i<(1<<blkcnt);++i){
ll newsiz=0,tmp=1;
int pop=popcount(i);
if(pop==1){
int blk=log2(i)+1;
tmp=blksiz[blk]*(blksiz[blk]-1)/2-(blksiz[blk]-1);
newsiz=blksiz[blk];
}else if(pop==2){
int blk1=log2(i&-i)+1,blk2=log2((i^(i&-i))&(-(i^(i&-i))))+1;
tmp=blksiz[blk1]*blksiz[blk2];
tmp=tmp*(tmp-1)/2;
newsiz=blksiz[blk1]+blksiz[blk2];
}else{
tmp=fac[pop-1];
for(int j=0;j<blkcnt;++j){
if((i>>j)&1){
tmp=tmp*blksiz[j+1]%mod*blksiz[j+1]%mod;
newsiz+=blksiz[j+1];
}
}
tmp=tmp*inv2%mod;
}
pisiz=newsiz;
for(int j=0;j<blkcnt;++j)if(!((i>>j)&1))pisiz=pisiz*blksiz[j+1]%mod;
int newblkcnt=blkcnt-popcount(i)+1;
if(newblkcnt==1){
(res2+=tmp)%=mod;
continue;
}
tmp=tmp*qpow(n,newblkcnt-2)%mod*pisiz%mod;
(res2+=tmp)%=mod;
}
return (res1+res2)%mod;
}
};