[学习笔记]FMT子集卷积
〇、模板测试链接
壹、说明
子集卷积解决的是这样一个问题,有 \(a,b\) 两个多项式,现在让你求 \(c\),其中 \(c\) 满足
\[c_k=\sum_{i\cap j=0,i\cup j=k}a_ib_j
\]
我们有比较朴素的枚举 \(k\) 的每个子集,定义 \(n=\log_2k\),那么这样的复杂度是 \(\mathcal O(3^n)\) 的,这个复杂度可能有点离谱.
我们考虑转换限制,定义 \(A_{i,j}\) 表示 \(a\) 的第 \(j\) 位有 \(i\) 个 \(1\),而 \(B,C\) 同理,那么我们有
\[C_{i,k}=\sum_{j=0}^i\sum_{x\cup y=k}^kA_{j,x}B_{i-j,y}
\]
因为 \(1\) 的个数是固定的,所以选定一个 \(a\) 之后我们可以唯一确定 \(b\) 对应的下标只有可能有多少个 \(1\),然后再根据拼在一起是 \(c\) 的集合,增加另一个限制,这样就把原式变换成了一个 \(\tt FWT\) 的标准形式,用或卷积就可以了.
时间复杂度 \(\mathcal O(2^nn^2)\).
贰、代码
const int maxn=1<<20;
const int mod=1e9+9;
int c[21][maxn+5],a[21][maxn+5],b[21][maxn+5];
int bitcnt[maxn+5];
int n,sz;
inline void fwt_or(int* f,const int n,const int opt=1){
for(int p=2;p<=n;p<<=1){
int len=p>>1;
for(int k=0;k<n;k+=p){
for(int i=k;i<k+len;++i){
f[i+len]=(f[i+len]+opt*f[i])%mod;
}
}
}
}
#define lowbit(i) (i&(-i))
inline void input(){
sz=readin(1);
n=1<<sz;
rep(i,1,n)bitcnt[i]=bitcnt[i-lowbit(i)]+1;
rep(i,0,n-1)a[bitcnt[i]][i]=readin(1);
rep(i,0,n-1)b[bitcnt[i]][i]=readin(1);
}
signed main(){
input();
rep(i,0,sz)fwt_or(a[i],n),fwt_or(b[i],n);
rep(i,0,sz)rep(j,0,i)rep(k,0,n-1)
c[i][k]=(c[i][k]+1ll*a[j][k]*b[i-j][k]%mod)%mod;
rep(i,0,sz)fwt_or(c[i],n,-1);
rep(i,0,n-1)writc((c[bitcnt[i]][i]+mod)%mod,' ');
return 0;
}