[学习笔记]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;
}
posted @ 2021-02-03 16:31  Arextre  阅读(90)  评论(0编辑  收藏  举报