F 是后缀数组,H 是分治 FFT,你管这叫 ABC?
题意:
给定 \(n\) 点 \(m\) 边无向图 \(G\),无自环,无重边。对于每个 \(k \in [2,n]\),试求 \(G\) 的子图个数,满足点 \(1\) 与点 \(k\) 是连通的。
Data range:\(n \le 17,m \le \frac{n(n-1)}{2}\) 。
分析:
一个月前教练讲过一道同类型同做法,比这道题还难不少的题:Topcoder15279-Spanning Subgraphs。
可惜我当时摸鱼没有补,导致 ABC 赛场上切完 E 罚坐 70 min。
容易想到状压求解。有一个显然的事实是,不能通过枚举边来转移。比如说边依次为 \((2,3),(1,2)\),就会假掉。因为你记录的肯定是和 \(1\) 连通的点,不然状态会炸掉。而如果通过枚举边来转移,那么如果 \(1-3\) 需要连通,我两条边肯定都得选,但是选第一条边的时候 \(2,3\) 都不与 \(1\) 连通,无法记录到。
所以直接考虑设 \(f(S)\) 为让点集 \(S\) 连通的子图数(\(1 \in S\))。考虑容斥,设 \(g(S)\) 为点集为 \(S\) 的子图数(不要求连通),那么 \(g(S)\) 的计算是容易的:计算所有边的两端点都在 \(S\) 的边数 \(cnt\),则 \(g(S)=2^{cnt}\)。那么有:
\[f(S)=g(S)-\sum_{1\in T\nsubseteq S}f(S)g(S\setminus T)
\]
奇怪的是,不管要不要求 \(1 \in T\)(不然会算重),AT上都能AC。我暂且蒙古。
那么这部分的总复杂度就是 \(3^n\) 的。考虑 \(g\) 的计算,显然是 \(2^nm\) 的,所以总复杂度为:\(O(3^n+2^nm)\)。
#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define op(x) ((x&1)?x+1:x-1)
#define odd(x) (x&1)
#define even(x) (!odd(x))
#define lc(x) (x<<1)
#define rc(x) (lc(x)|1)
#define lowbit(x) (x&-x)
#define Max(a,b) (a>b?a:b)
#define Min(a,b) (a<b?a:b)
#define next Cry_For_theMoon
#define il inline
#define pb(x) push_back(x)
#define is(x) insert(x)
#define sit set<int>::iterator
#define mapit map<int,int>::iterator
#define pi pair<int,int>
#define ppi pair<int,pi>
#define pp pair<pi,pi>
#define fr first
#define se second
#define vit vector<int>::iterator
#define mp(x,y) make_pair(x,y)
typedef long long ll;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef double db;
using namespace std;
const int MAXN=18,mod=998244353;
int n,m,edge[MAXN][MAXN];
ll g[(1<<18)],f[(1<<18)];
int main(){
cin>>n>>m;
rep(i,1,m){
ll u,v;cin>>u>>v;
edge[u][v]=edge[v][u]=1;
}
rep(i,0,(1<<n)-1){
g[i]=1;
rep(u,1,n){
rep(v,u+1,n){if(!edge[u][v])continue;
if((i&(1<<(u-1))) && (i&(1<<(v-1)))){
g[i]=g[i]*2%mod;
}
}
}
}
rep(i,1,(1<<n)-1){
if(!(i&1))continue;
f[i]=g[i];
for(ll j=i;j;j=(j-1)&i){
if(j==i)continue;
if(!(j&1))continue;
f[i]=(f[i]-f[j]*g[i^j]%mod+mod)%mod;
}
}
rep(k,2,n){
ll ans=0;
rep(i,1,(1<<n)-1){
if(!(i&1) || !(i&(1<<(k-1))))continue;
ans=(ans+f[i]*g[((1<<n)-1)^i]%mod)%mod;
}
printf("%lld\n",ans);
}
return 0;
}