前置知识:FMT 或卷积
FMT
FMT(x)=i∣x∑xi
1 2 3
| void FMT(int *A){ for(int i=0;i<=n;i++) for(int j=0;j<=up;j++) if(j&(1<<i)) (A[j]+=A[j^(1<<i)])%=mod; }
|
IFMT
FMT的逆运算。
1 2 3
| void IFMT(int *A){ for(int i=0;i<=n;i++) for(int j=0;j<=up;j++) if(j&(1<<i)) (A[j]-=A[j^(1<<i)])%=mod; }
|
或卷积
C=A∣orB −>Ci=j∣k==i∑AjBk
有
FMT(C)=FMT(A)∗FMT(B)
这里的乘是点乘。
证明的话,带入拆开即可。
这样IFMT一遍就可以求C了。
子集卷积
题目传送门
多了个麻烦的限制,我们考虑多加一维。
A(pc(i),i)表示状态为i,有pc(i)个1。
那么:
Ci=i==j+k∑Aj∣orBk
暴力枚举卷积是O(n32n)
把卷积写成FMT形式。
把求和号移到后面:
Ci=IFMT(i∑FMT(Aj)∗FMT(Bi−j))
预处理FMT,点成、转回来。每一个部分都是O(n22n)。
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include<bits/stdc++.h> using namespace std; const int M=1e6+1e5+5,N=21,mod=1e9+9; int n,m,up,a[N][M],b[N][M],c[N][M],cnt[M]; void FMT(int *A){ for(int i=0;i<=n;i++) for(int j=0;j<=up;j++) if(j&(1<<i)) (A[j]+=A[j^(1<<i)])%=mod; } void IFMT(int *A){ for(int i=0;i<=n;i++) for(int j=0;j<=up;j++) if(j&(1<<i)) (A[j]-=A[j^(1<<i)])%=mod; } signed main(){ ios::sync_with_stdio(false); cin>>n;up=(1<<n)-1;m=(1<<n); for(int i=1;i<=up;i++) cnt[i]=cnt[i&(i-1)]+1; for(int i=0;i<m;i++) cin>>a[cnt[i]][i]; for(int i=0;i<m;i++) cin>>b[cnt[i]][i]; for(int i=0;i<=n;i++) FMT(a[i]),FMT(b[i]); for(int i=0;i<=n;i++) for(int s=0;s<=up;s++) for(int j=0;j<=i;j++) (c[i][s]+=(1ll*a[j][s]*b[i-j][s])%mod)%=mod; for(int i=0;i<=n;i++) IFMT(c[i]); for(int i=0;i<m;i++) cout<<(c[cnt[i]][i]%mod+mod)%mod<<" "; return 0; }
|