引
幸亏状压DP的数据范围明显,不然自己估计想不出来
解法
先考虑答案如何计算,比如计算点
k
k
k的答案,可以枚举包含
1
,
k
1,k
1,k 的联通子图,然后每一个联通子图的方案数乘上无关的边就行。
形式化地, 设
G
G
G 为全图的点集 ,
S
S
S 为
G
G
G 的一个子图,
f
S
f_{S}
fS 表示
S
S
S 的联通子图的方案数,
g
S
g_{S}
gS 为S的子图数,那我们有:
a
n
s
k
=
∑
{
1
,
k
}
∈
S
f
S
∗
g
∁
G
S
ans_k=\sum_{\{1,k\}\in S} f_{S} *g_{\complement_{G}S }
ansk={1,k}∈S∑fS∗g∁GS
考虑计算
f
,
g
f,g
f,g 由于点很少,我们用二进制数表示状态,用 状压
D
P
DP
DP 解决
g
g
g 用点的个数就可以求,
2
c
n
t
S
2^{cnt_S}
2cntS ,
c
n
t
S
cnt_S
cntS 是
S
S
S 的点数
f
S
=
g
S
−
∑
1
∈
T
⊂
S
f
T
∗
g
∁
S
T
f_{S}=g_{S}-\sum_{1\in T\subset S} f_{T} *g_{\complement_{S}T}
fS=gS−1∈T⊂S∑fT∗g∁ST
Code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=(1<<17)+7,mod=998244353;
int n,m;
ll g[N],f[N],t[205];
vector<int> G[N];
void Get_g() {
g[0]=1;
for(int mask=1;mask<(1<<n);mask++) {
ll tmp=0;
for(int i=0;i<n;i++) if(mask>>i&1) {
for(int j:G[i+1])
if(mask>>(j-1)&1) tmp++;
} g[mask]=t[(tmp>>1)];
}
}
void Get_f() {
for(int mask=1;mask<(1<<n);mask+=2) {
for(int sub=mask;sub;sub=(sub-1)&mask) if(sub &1 ){
f[mask]=(f[mask] + f[sub] * g[mask^sub] %mod) %mod;
}
f[mask]=((g[mask]-f[mask])%mod+mod)%mod;
}
}
int main() {
scanf("%d%d",&n,&m);
t[0]=1; for(int i=1;i<=200;i++) t[i]=(t[i-1]<<1)%mod;
for(int i=1,u,v;i<=m;i++) {
scanf("%d%d",&u,&v) ;
G[u].push_back(v),G[v].push_back(u);
}
Get_g(),Get_f();
for(int i=2;i<=n;i++) {
ll tmp=0;
for(int mask=1;mask<(1<<n);mask+=2)
if(mask>>(i-1)&1) tmp=(tmp+f[mask]*g[((1<<n)-1)^mask] %mod) %mod;
printf("%lld\n",tmp);
}
}