废话
是在 NOI Online 3 上被强行安利的科技……
模板题在你谷上都只有
115
115
115 人过qwq…… update:被科普后现在有近
700
700
700 人了qwq……
正题
是用来做这样的卷积的:
c
n
=
∑
i
&
j
=
0
i
∣
j
=
n
a
i
×
b
j
c_n=\sum_{\begin{aligned}i\&j=0\\i|j=n\end{aligned}} a_i\times b_j
cn=i&j=0i∣j=n∑ai×bj
如果没有 i & j = 0 i\&j=0 i&j=0 这个条件那么就是个 F M T FMT FMT 了,考虑怎么处理这个东西。
不妨将每个数组增加一维,设 a c n t ( n ) , n a_{cnt(n),n} acnt(n),n 表示原来的 a n a_n an,其中 c n t ( n ) cnt(n) cnt(n) 表示集合 n n n 内有多少个元素,在二进制下就是 1 1 1 的个数。
那么 i & j = 0 i\&j=0 i&j=0 这个限制就变成了 c n t ( i ) + c n t ( j ) = c n t ( n ) cnt(i)+cnt(j)=cnt(n) cnt(i)+cnt(j)=cnt(n)。
然后考虑将每个 a i a_i ai 进行 F M T FMT FMT 的正变换,然后像平常一样 a , b a,b a,b 对应位置乘起来得到 c c c,不过要按照 c n t ( i ) + c n t ( j ) = c n t ( n ) cnt(i)+cnt(j)=cnt(n) cnt(i)+cnt(j)=cnt(n) 的规则来加。
最后把每个 c i c_i ci 再逆变换回去,每个 c c n t ( i ) , i c_{cnt(i),i} ccnt(i),i 就对应我们要的 c i c_i ci。
这题有点神奇,用 f r e a d fread fread 来优化会变慢……
代码如下:
#include <cstdio>
#define mod 1000000009
int n,cnt[1<<20];
int A[21][1<<20],B[21][1<<20],C[21][1<<20];
int add(int x,int y){return x+y<mod?x+y:x+y-mod;}
void FWT(int *f,int type)
{
for(int mid=1;mid<(1<<n);mid<<=1)
for(int j=0;j<(1<<n);j+=(mid<<1))
for(int i=j;i<j+mid;i++)f[i+mid]=add(f[i+mid],add(type*f[i],mod));
}
int main()
{
scanf("%d",&n);
for(int i=1;i<(1<<n);i++)cnt[i]=cnt[i-(i&(-i))]+1;
for(int i=0;i<(1<<n);i++)scanf("%d",&A[cnt[i]][i]);
for(int i=0;i<(1<<n);i++)scanf("%d",&B[cnt[i]][i]);
for(int i=0;i<=n;i++)FWT(A[i],1),FWT(B[i],1);
for(int i=0;i<=n;i++)for(int j=0;j<=i;j++)
for(int k=0;k<(1<<n);k++)
C[i][k]=add(C[i][k],1ll*A[j][k]*B[i-j][k]%mod);
for(int i=0;i<=n;i++)FWT(C[i],-1);
for(int i=0;i<(1<<n);i++)printf("%d ",C[cnt[i]][i]);
}