[ABC213G] Connectivity 2 题解
好好好。
我们设当前处理 \(i\) 的答案,那么最后的图就可以分成两个部分:\(1\) 所在的联通块和其他,根据乘法原理,答案就是它们二者方案的乘积。
设 \(f_s\) 表示集合 \(s\) 中所有点联通时图的情况数,\(g_s\) 表示集合 \(s\) 中所有点不一定联通时图的情况数,则有:
\[ans_i=\sum\limits_{\{1,i\}\in s}f_s\times g_{s'}
\]
其中 \(s'\) 表示 \(s\) 相对于 \(\{1,2,\dots,n\}\) 这个集合的补集。预处理 \(f_s,g_s\) 的情况下,该部分时间复杂度为 \(O(n2^n)\)。
那么现在就要求 \(f_s\) 和 \(g_s\) 了。
\(g_s\) 好说,设 \(dis_{i,j}\) 表示 \(i,j\) 间有没有边,则:
\[g_s=2^{\sum\limits_{i\ \in\ s}\sum\limits_{j\ \in\ s}dis_{i,j}}
\]
时间复杂度 \(O(n^22^n)\)。
考虑 \(f_s\)。我们暴力出奇迹,强制设一个点 \(v\in s\),将原图划分成两张图,一张有 \(v\),必须连通;一张没 \(v\),不要求连通。这种情况下,我们已经构造出了所有的不连通图情况,那么用 \(g_s\) 减去它,就是 \(f_s\) 的值,即:
\[f_s=g_s-\sum\limits_{t\subsetneq s} f_t\times g_{t'}
\]
由于要枚举子集,所以时间复杂度 \(O(3^n)\)。
总体时间复杂度 \(O(3^n+n^22^n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=20,M=1<<17;
const int p=998244353;
int n,dis[N][N],f[M];
int m,g[M],tw[N*N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m,g[0]=1,tw[0]=1;
for(int i=1,u,v;i<=m;i++)
cin>>u>>v,dis[u][v]=1,tw[i]=tw[i-1]*2%p;
for(int s=1,cnt=0;s<(1<<n);s++,cnt=0){
for(int i=1;i<n;i++)
if((s>>(i-1))&1)
for(int j=n;j>i;j--)
cnt+=((s>>(j-1))&1)*dis[i][j];
f[s]=g[s]=tw[cnt];int v=log2(s)+1;
for(int t=(s&(s-1));t;t=((t-1)&s))
if((1<<(v-1))&t) f[s]=(f[s]-1ll*f[t]*g[s^t]%p+p)%p;
}for(int i=2,sum=0;i<=n;i++,sum=0){
for(int s=1;s<(1<<n);s+=2) if((s>>(i-1))&1)
sum=(sum+1ll*f[s]*g[((1<<n)-1)^s]%p)%p;
cout<<sum<<"\n";
}return 0;
}