题目描述
给定两个长度为 2 n 2^n 2n 的序列 a 0 , a 1 , ⋯ , a 2 n − 1 a_0,a_1,\cdots,a_{2^n-1} a0,a1,⋯,a2n−1 和 b 0 , b 1 , ⋯ , b 2 n − 1 b_0,b_1,\cdots,b_{2^n-1} b0,b1,⋯,b2n−1,你需要求出一个序列 c 0 , c 1 , ⋯ , c 2 n − 1 c_0,c_1,\cdots,c_{2^n-1} c0,c1,⋯,c2n−1,其中 c k c_k ck 满足:
c k = ∑ i & j = 0 i ∣ j = k a i b j c_k=\sum_{\substack{i\& j=0 \\\ i\mid j=k}} a_i b_j ck=i&j=0 i∣j=k∑aibj
其中 ∣ ~\mid~ ∣ 表示按位或, & \& &表示按位与。
答案对 1 0 9 + 9 10^9+9 109+9 取模。
过程
在没有条件 i & j = 0 i \& j = 0 i&j=0 时,这就是一个普通的 FWT 卷积,考虑如何消掉这个条件。
若有一个序列 f f f,我们重新定义它的值为 f ′ f' f′, p ( x ) p(x) p(x) 表示 x x x 二进制表示下 1 1 1 的个数。
f j , i ′ { f i , p ( i ) = j , 0 , o t h e r w i s e . f'_{j,i}\left\{\begin{array}{l} f_i,& p(i) = j,\\ 0, & otherwise. \end{array}\right. fj,i′{fi,0,p(i)=j,otherwise.
于是发现当 p ( i ) + p ( j ) = p ( k ) p(i) + p(j) = p(k) p(i)+p(j)=p(k) 且 i ∣ j = k i | j = k i∣j=k 时,一定满足 i & j = 0 i \& j = 0 i&j=0,所以我们只需在 FWT 的基础上多增加一维即可。
时间复杂度 O ( n 2 2 n ) O(n^2 2^n) O(n22n) 。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 21, mod = 1e9 + 9;
int a[N][1 << N], b[N][1 << N], c[N][1 << N];
int p(int x) {return __builtin_popcount(x);}
void OR(int *a, int n, int x) {
for (int o = 2, k = 1; o <= n; o <<= 1, k <<= 1)
for (int i = 0; i < n; i += o)
for (int j = 0; j < k; j ++ )
a[i + j + k] = ((a[i + j + k] + a[i + j] * x) % mod + mod) % mod;
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < (1 << n); i ++ ) scanf("%d", &a[p(i)][i]);
for (int i = 0; i < (1 << n); i ++ ) scanf("%d", &b[p(i)][i]);
for (int i = 0; i <= n; i ++ ) OR(a[i], 1 << n, 1), OR(b[i], 1 << n, 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] = (c[i][k] + 1ll * a[j][k] * b[i - j][k] % mod) % mod;
OR(c[i], 1 << n, -1);
}
for (int i = 0; i < (1 << n); i ++ ) printf("%d ", c[p(i)][i]);
return 0;
}