【题解】abc213_g Connectivity 2
abc213_g Connectivity 2
题意
给定一张 \(n\) 个点 \(m\) 条边的简单无向图 \(G\),从 \(G\) 中删除若干条边(可以是 \(0\))得到子图 \(H\),对于每个整数 \(k\),求出有多少张子图 \(H\) 满足 \(1\) 与 \(k\) 联通。
答案对 \(998244353\) 取模。
\(n\le 17,m\le \frac{n(n-1)}{2}\)。
题解
知识点:动态规划。
图论计数的经典状压 dp,值得一做。
设 \(f(S)\) 表示以 \(S\) 为顶点集的联通子图个数。
设 \(g(S)\) 表示以 \(S\) 为顶点集的所有子图个数。
则对于整数 \(k\),答案为:
其中 \(V\) 为 \(G\) 的顶点集,\(S\setminus T\) 表示属于 \(S\) 但不属于 \(T\) 的顶点集,这里默认的是 \(S\) 与 \(S\setminus T\) 的点之间相互不连边,所以能做到不重不漏。
\(g(S)\) 是好求的,设连接顶点集 \(S\) 内部的边的数量为 \(E(S)\),则 \(g(S)=2^{E(S)}\),通过枚举每一条边贡献到了哪个集合,预处理可以做到 \(O(m\times 2^n)\)。
初始时,\(f(\emptyset)=0\)。
求 \(f(S)\) 使用正难则反,用以 \(S\) 为顶点集的所有子图个数减去不连通的个数,为了防止记重记漏,这里钦定一个只有一个点的顶点集 \(v=\operatorname{lowbit(S)}\),所枚举的 \(T\) 必须包含 \(v\),意义在于枚举 \(v\) 所在连通块的情况,有转移:
这里默认的也是 \(S\) 与 \(S\setminus T\) 的点之间相互不连边。
枚举子集的总复杂度是 \(O(3^n)\) 的,最后计算答案的复杂度是 \(O(n\times 2^n)\) 的,所以总复杂度是 \(O(3^n + m\times 2^n + n\times 2^n)\)。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 18
#define M (131072+5)
#define int long long
#define lb(x) (x&-x)
const int mod=998244353;
int n,m,f[M],g[M],ans[N],tot;
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
tot=(1<<n)-1;
rep(i,0,tot){
g[i]=1;
}
rep(i,1,m){
int u,v;
cin>>u>>v;
rep(s,0,tot){
if((s>>(u-1)&1)&&(s>>(v-1)&1)){
g[s]=g[s]*2%mod;
}
}
}
f[0]=0;
rep(s,1,tot){
int v=lb(s);
f[s]=g[s];
int t=(s-1)&s;
while(t){
if(!(t&v)){
t=(t-1)&s;
continue;
}
f[s]=(f[s]-f[t]*g[s^t]%mod+mod)%mod;
t=(t-1)&s;
}
}
rep(s,0,tot){
rep(k,2,n){
if((s>>(k-1)&1)&&(s&1)){
ans[k]=(ans[k]+f[s]*g[tot-s]%mod)%mod;
}
}
}
rep(i,2,n){
cout<<ans[i]<<"\n";
}
return 0;
}