LOJ#152. 子集卷积

终于 get 到了子集卷积的正确姿势,以前竟然都是用异或卷积写的.  

众所周知,异或卷积由于涉及到乘法所以会比较慢,那我们用或卷积就好了!   

code: 

#include <bits/stdc++.h>     
#define N (1<<21)  
#define ll long long 
#define mod 1000000009    
#define lowbit(x) ((x)&(-(x)))
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;     
int n,inv2,lim;      
int f[N],g[N],A[21][N],B[21][N],C[21][N],cnt[N]; 
int qpow(int x,int y) 
{
    int tmp=1;  
    for(;y;y>>=1,x=(ll)x*x%mod) if(y&1) tmp=(ll)tmp*x%mod;  
    return tmp; 
}  
int INV(int x) { return qpow(x,mod-2); }  
void FWT(int *a) 
{   
    for(int len=1;len<lim;len<<=1) 
        for(int i=0;i<lim;i+=len<<1) 
            for(int j=0;j<len;++j) 
                (a[i+j+len]+=a[i+j])%=mod;      
}
void IFWT(int *a) 
{      
    for(int len=1;len<lim;len<<=1) 
        for(int i=0;i<lim;i+=len<<1) 
            for(int j=0;j<len;++j)  
                (a[i+j+len]+=mod-a[i+j])%=mod;  

}    
char *p1,*p2,buf[100000];  
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)  
int rd() 
{
    int x=0; char c=nc(); 
    while(c<48) c=nc();  
    while(c>47) x=(((x<<2)+x)<<1)+(c^48),c=nc();   
    return x;  
}   
void print(int x) { if(x>=10) print(x/10); putchar(x%10+'0');}
int main() 
{ 
    // setIO("input");                 
    n=rd(),lim=1<<n;     
    for(int i=0;i<lim;++i) f[i]=rd(); 
    for(int i=0;i<lim;++i) g[i]=rd();      
    for(int i=1;i<lim;++i) cnt[i]=cnt[i-lowbit(i)]+1;  
    for(int i=0;i<lim;++i) A[cnt[i]][i]=f[i],B[cnt[i]][i]=g[i];          
    for(int i=0;i<=n;++i)  FWT(A[i]),FWT(B[i]);   
    for(int i=0;i<=n;++i)    
    {
        for(int j=0;j<=n;++j)   
            if(i+j<=n) 
            {    
                for(int k=0;k<lim;++k) (C[i+j][k]+=(ll)A[i][k]*B[j][k]%mod)%=mod;  
            }    
    }          
    for(int i=0;i<=n;++i)  IFWT(C[i]);          
    for(int i=0;i<lim;++i) print(C[cnt[i]][i]),putchar(' ');      
    return 0; 
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值