子集卷积学习笔记
子集卷积学习笔记
\[h_S=\sum_{L\cup R=S,L\cap S=\emptyset} f_Lg_R
\]
设
\[f(i,S)=\sum_{T\in S,|T|=i} f(T)
\]
这个对于每个 \(i\) 做一次FMT即可
然后有
\[h(i,S)=\sum_{j=0}^i f(j,S)g(i-j,S)
\]
交集为空集,那么元素个数加起来应该等于并集的元素个数
最后直接IFWT即可
其实本质就是按元素个数归类了一下。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,pos=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return pos?x:-x;
}
#define ll long long
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define ROF(i,a,b) for(register int i=(a);i>=(b);--i)
const int mod = 1e9+9;
#define add(a,b) a=a+b>=mod?a+b-mod:a+b
#define del(a,b) a=a<b?a-b+mod:a-b
const int N = 1<<20;
int m,n,f[21][N],g[21][N],h[21][N],bi[N];
void FWT(int *f,int opt){
for(register int l=2;l<=n;l<<=1){
int mid=l/2;
for(int *g=f;g!=f+n;g+=l){
FOR(i,0,mid-1){
if(opt==1) add(g[i+mid],g[i]);
else del(g[i+mid],g[i]);
}
}
}
}
int main(){
m=read();n=1<<m;
FOR(i,0,n-1) bi[i]=bi[i>>1]+(i&1),f[bi[i]][i]=read();
FOR(i,0,n-1) g[bi[i]][i]=read();
FOR(i,0,m){
FWT(f[i],1);FWT(g[i],1);
}
FOR(i,0,m){
FOR(j,0,n-1){
FOR(k,0,i){
add(h[i][j],1ll*f[k][j]*g[i-k][j]%mod);
}
}
}
FOR(i,0,m){
FWT(h[i],-1);
}
FOR(i,0,n-1){
printf("%d ",h[bi[i]][i]);
}
return 0;
}