解析
完全可以当一道 DP 题而不是模板来做。
首先第一个条件: i∣j=ki|j=ki∣j=k 比较简单,直接上FWT板子即可。
考虑第二个条件:i&j=0i\&j=0i&j=0。若设 ∣x∣|x|∣x∣ 表示二进制下 1 的个数,那么就有: ∣i∣+∣j∣=∣k∣|i|+|j|=|k|∣i∣+∣j∣=∣k∣。
那么考虑设计dp:dpi,jdp_{i,j}dpi,j 表示二进制有 iii 个 1,或运算结果为 jjj 的答案。
就有 dpk,i∣j=∑pk∑i,jfp,igk−p,jdp_{k,i|j}=\sum_{p}^k\sum_{i,j}f_{p,i}g_{k-p,j}dpk,i∣j=p∑ki,j∑fp,igk−p,j
然后把 nnn 个 f,gf,gf,g 变换后 n2n^2n2 暴力卷积成答案再变换回去。
总复杂度 O(n22n)O(n^22^n)O(n22n)。
代码
(阴间题卡longlong的常数…)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
const int N=1e6+1e5;
const int mod=1e9+9;int n,lim;
inline int calc(int x){int res(0);while(x){++res;x-=x&-x;}return res;
}
inline void Or(int *x,int lim,int op){for(int l=1;l<lim;l<<=1){for(int st=0;st<lim;st+=l*2){for(int i=0;i<l;i++){if(op==1) (x[st+i+l]+=x[st+i])%=mod;else (x[st+i+l]+=mod-x[st+i])%=mod;}}}return;
}
int A[21][N],B[21][N],C[21][N];
void write(int x){if(x>9) write(x/10);putchar('0'+x%10);return;
}
signed main(){
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifn=read();lim=(1<<n);for(int i=0;i<lim;i++) A[calc(i)][i]=read();for(int i=0;i<lim;i++) B[calc(i)][i]=read();for(int i=0;i<=n;i++) Or(A[i],lim,1);for(int i=0;i<=n;i++) Or(B[i],lim,1);/*for(int k=0;k<=n;k++){for(int i=0;i<=k;i++){for(int x=0;x<lim;x++) C[k][x]=(C[k][x]+A[i][x]*B[k-i][x])%mod;} }*/for(int i=0;i<=n;i++){for(int x=0;x<=lim;x++){if(!A[i][x]) continue;for(int j=0;j+i<=n;j++) C[i+j][x]=(C[i+j][x]+1ll*A[i][x]*B[j][x]%mod)%mod;}//debug("%d\n",i);}/*for(int i=0;i<=n;i++){for(int j=0;j<=i;j++){for(int k=0;k<lim;k++) C[i][k]=(C[i][k]+1ll*A[j][k]*B[i-j][k]%mod)%mod;}//debug("%d\n",i);}*/for(int i=0;i<=n;i++) Or(C[i],lim,-1);for(int i=0;i<lim;i++) write(C[calc(i)][i]),putchar(' ');//for(int i=0;i<lim;i++) printf("%d ",C[calc(i)][i]);return 0;
}
/*
*/