简介
FWT 是用来求位运算卷积的工具,对于长度为 2 n 2^n 2 n 的数组 A , B , C A,B,C A , B , C ,它们的元素类型通常是 Z p \mathbb Z_p Z p 或 Z \mathbb Z Z ,若:
∀ 0 ≤ k ≤ 2 n − 1 , C k = ∑ i = 0 2 n − 1 ∑ j = 0 2 n − 1 A i B j [ i ∘ j = k ] \forall 0\le k\le 2^n-1, C_k=\sum_{i=0}^{2^n-1}\sum_{j=0}^{2^n-1} A_iB_j[i\circ j=k]
∀0 ≤ k ≤ 2 n − 1 , C k = i = 0 ∑ 2 n − 1 j = 0 ∑ 2 n − 1 A i B j [ i ∘ j = k ]
其中 [ − ] [-] [ − ] 是艾弗森括号,那么称 C C C 是 A , B A,B A , B 关于 ∘ \circ ∘ 运算的卷积,记作 C = A ∗ B C=A*B C = A ∗ B 。
我们希望找到一个可逆的变换 F \mathcal F F ,使得:
F ( C ) = F ( A ) ⋅ F ( B ) \mathcal F(C)=\mathcal F(A)\cdot \mathcal F(B)
F ( C ) = F ( A ) ⋅ F ( B )
其中 ⋅ \cdot ⋅ 是逐元素的乘法。
其次,我们也希望这是一个线性变换,即:
F ( k A + l B ) = k F ( A ) + l F ( B ) \mathcal F(kA+lB)=\mathcal kF(A)+\mathcal lF(B)
F ( k A + lB ) = k F ( A ) + lF ( B )
其中 k , l k,l k , l 是任意常数,加法是逐元素加法,乘法是数量乘法。
变换
下面的问题就是,怎么找到这样的变换?
设 c i , j c_{i,j} c i , j 是 A j A_j A j 对 F i ( A ) \mathcal F_i(A) F i ( A ) 的贡献系数,我们可以先构造出这样的一种变换:
F i ( A ) = ∑ j = 0 2 n − 1 c i , j A j \mathcal F_i(A)=\sum_{j=0}^{2^n-1} c_{i,j} A_j
F i ( A ) = j = 0 ∑ 2 n − 1 c i , j A j
其中 c i , j c_{i,j} c i , j 满足这样的性质:
设 i i i 的二进制表示是 i n − 1 ⋯ i 1 i 0 i_{n-1}\cdots i_1i_0 i n − 1 ⋯ i 1 i 0 ,j j j 的二进制表示是 j n − 1 ⋯ j 1 j 0 j_{n-1}\cdots j_1j_0 j n − 1 ⋯ j 1 j 0 ,那么 c i , j = ∏ k = 0 n − 1 c i k , j k c_{i,j}=\prod_{k=0}^{n-1} c_{i_k,j_k} c i , j = ∏ k = 0 n − 1 c i k , j k 。
对于任意的 i , j i,j i , j ,c i , j ∈ { − 1 , 0 , 1 } c_{i,j}\in \{-1,0,1\} c i , j ∈ { − 1 , 0 , 1 } 。
为什么这么设?因为这是一个易于处理的假设,因为只要知道了 c 0 / 1 , 0 / 1 = ± 1 / 0 c_{0/1,0/1}=\pm1/0 c 0/1 , 0/1 = ± 1/0 ,就能求出所有的 c i , j c_{i,j} c i , j ,而前者一共只有 3 4 3^4 3 4 种可能性。
根据我们的需求,展开 F i ( C ) = F i ( A ) F i ( B ) \mathcal F_i(C)=\mathcal F_i(A)\mathcal F_i(B) F i ( C ) = F i ( A ) F i ( B ) ,可以得到:
F i ( C ) = ∑ p = 0 2 n − 1 C p c i , p = ∑ p = 0 2 n − 1 ∑ j = 0 2 n − 1 ∑ k = 0 2 n − 1 A j B k c i , p [ j ∘ k = p ] = ∑ j = 0 2 n − 1 ∑ k = 0 2 n − 1 A j B k ( ∑ p = 0 2 n − 1 c i , p [ j ∘ k = p ] ) = ∑ j = 0 2 n − 1 ∑ k = 0 2 n − 1 A j B k c i , j ∘ k F i ( A ) F i ( B ) = ∑ j = 0 2 n − 1 ∑ k = 0 2 n − 1 A j B k c i , j c i , k \begin{aligned}
\mathcal F_i(C)&=\sum_{p=0}^{2^n-1} C_pc_{i,p}\\
&=\sum_{p=0}^{2^n-1}\sum_{j=0}^{2^n-1}\sum_{k=0}^{2^n-1} A_jB_k c_{i,p}[j\circ k=p]\\
&=\sum_{j=0}^{2^n-1}\sum_{k=0}^{2^n-1} A_jB_k \left(\sum_{p=0}^{2^n-1}c_{i,p}[j\circ k=p]\right)\\
&=\sum_{j=0}^{2^n-1}\sum_{k=0}^{2^n-1} A_jB_k c_{i,j\circ k}\\
\mathcal F_i(A)\mathcal F_i(B)&=\sum_{j=0}^{2^n-1}\sum_{k=0}^{2^n-1} A_jB_kc_{i,j}c_{i,k}
\end{aligned}
F i ( C ) F i ( A ) F i ( B ) = p = 0 ∑ 2 n − 1 C p c i , p = p = 0 ∑ 2 n − 1 j = 0 ∑ 2 n − 1 k = 0 ∑ 2 n − 1 A j B k c i , p [ j ∘ k = p ] = j = 0 ∑ 2 n − 1 k = 0 ∑ 2 n − 1 A j B k ( p = 0 ∑ 2 n − 1 c i , p [ j ∘ k = p ] ) = j = 0 ∑ 2 n − 1 k = 0 ∑ 2 n − 1 A j B k c i , j ∘ k = j = 0 ∑ 2 n − 1 k = 0 ∑ 2 n − 1 A j B k c i , j c i , k
因此 c i , j c i , k = c i , j ∘ k c_{i,j}c_{i,k}=c_{i,j\circ k} c i , j c i , k = c i , j ∘ k 是满足 F ( C ) = F ( A ) ⋅ F ( B ) \mathcal F(C)=\mathcal F(A)\cdot \mathcal F(B) F ( C ) = F ( A ) ⋅ F ( B ) 的充要条件。
当我们在寻找逆变换之前,我们来看如何快速地求出 F i ( A ) \mathcal F_i(A) F i ( A ) 。
由于这和二进制相关,所以我们按照 0 ∼ 2 n − 1 − 1 0\sim 2^{n-1}-1 0 ∼ 2 n − 1 − 1 和 2 n − 1 ∼ 2 n − 1 2^{n-1}\sim 2^n-1 2 n − 1 ∼ 2 n − 1 分组,设 i i i 在二进制从低到高的第 n n n 位是 i 0 i_0 i 0 ,那么:
F i ( A ) = ∑ j = 0 2 n − 1 − 1 c i , j A j + ∑ j = 2 n − 1 2 n − 1 c i , j A j = c i 0 , 0 ( ∑ j = 0 2 n − 1 − 1 c i − 2 n − 1 i 0 , j A j ) + c i 0 , 1 ( ∑ j = 0 2 n − 1 − 1 c i − 2 n − 1 i 0 , j A j + 2 n − 1 ) = c i 0 , 0 F i − 2 n − 1 i 0 ( A L ) + c i 0 , 1 F i − 2 n − 1 i 0 ( A R ) \begin{aligned}
\mathcal F_i(A)&=\sum_{j=0}^{2^{n-1}-1}c_{i,j}A_j+\sum_{j=2^{n-1}}^{2^n-1}c_{i,j}A_j\\
&=c_{i_0,0}\left(\sum_{j=0}^{2^{n-1}-1} c_{i-2^{n-1}i_0,j} A_{j}\right)+c_{i_0,1}\left(\sum_{j=0}^{2^{n-1}-1}c_{i-2^{n-1}i_0,j}A_{j+2^{n-1}}\right)\\
&=c_{i_0,0}\mathcal F_{i-2^{n-1}i_0}(A^L)+c_{i_0,1}\mathcal F_{i-2^{n-1}i_0} (A^R)
\end{aligned}
F i ( A ) = j = 0 ∑ 2 n − 1 − 1 c i , j A j + j = 2 n − 1 ∑ 2 n − 1 c i , j A j = c i 0 , 0 j = 0 ∑ 2 n − 1 − 1 c i − 2 n − 1 i 0 , j A j + c i 0 , 1 j = 0 ∑ 2 n − 1 − 1 c i − 2 n − 1 i 0 , j A j + 2 n − 1 = c i 0 , 0 F i − 2 n − 1 i 0 ( A L ) + c i 0 , 1 F i − 2 n − 1 i 0 ( A R )
其中 F ( A L ) \mathcal F(A^L) F ( A L ) 和 F ( A R ) \mathcal F(A^{R}) F ( A R ) 代表将 A 0 ∼ A 2 n − 1 − 1 A_0\sim A_{2^{n-1}-1} A 0 ∼ A 2 n − 1 − 1 和 A 2 n − 1 ∼ A 2 n − 1 A_{2^{n-1}}\sim A_{2^n-1} A 2 n − 1 ∼ A 2 n − 1 看作两个独立的数组解出的子问题的答案。
满足 i − 2 n − 1 i 0 = m i-2^{n-1}i_0=m i − 2 n − 1 i 0 = m (m m m 是某个常数)的 i i i 有且仅有两个 i L , i R i_L,i_R i L , i R ,它们的 i 0 i_0 i 0 对应着 0 0 0 和 1 1 1 。
也就是说:
F i L ( A ) = c 0 , 0 F m ( A L ) + c 0 , 1 F m ( A R ) F i R ( A ) = c 1 , 0 F m ( A L ) + c 1 , 1 F m ( A R ) \begin{aligned}
\mathcal F_{i_L}(A)&=c_{0,0} \mathcal F_m(A^L)+c_{0,1}\mathcal F_m(A^R)\\
\mathcal F_{i_R}(A)&=c_{1,0}\mathcal F_m(A^L)+c_{1,1}\mathcal F_m(A^R)
\end{aligned}
F i L ( A ) F i R ( A ) = c 0 , 0 F m ( A L ) + c 0 , 1 F m ( A R ) = c 1 , 0 F m ( A L ) + c 1 , 1 F m ( A R )
如果矩阵 [ c 0 , 0 c 0 , 1 c 1 , 0 c 1 , 1 ] \begin{bmatrix}c_{0,0}&c_{0,1}\\c_{1,0}&c_{1,1}\end{bmatrix} [ c 0 , 0 c 1 , 0 c 0 , 1 c 1 , 1 ] 可逆,那么这个变换自然就可逆。
当 ∘ \circ ∘ 是按位与运算时,c 0 , 0 , c 0 , 1 , c 1 , 0 , c 1 , 1 = 1 , 1 , 0 , 1 c_{0,0},c_{0,1},c_{1,0},c_{1,1}=1,1,0,1 c 0 , 0 , c 0 , 1 , c 1 , 0 , c 1 , 1 = 1 , 1 , 0 , 1 是满足要求的;
当 ∘ \circ ∘ 是按位或运算时,1 , 0 , 1 , 1 1,0,1,1 1 , 0 , 1 , 1 是满足要求的;
当 ∘ \circ ∘ 是按位异或运算时,1 , 1 , 1 , − 1 1,1,1,-1 1 , 1 , 1 , − 1 是满足要求的。
逆变换
接下来我们看,具体如何进行逆变换。
对于变换 F \mathcal F F ,我们想找到一个 G \mathcal G G 满足:
G i ( F ( A ) ) = A i \mathcal G_i(\mathcal F(A))= A_i
G i ( F ( A )) = A i
设 G \mathcal G G 对应的贡献系数是 d i , j d_{i,j} d i , j ,那么展开可以得到:
G i ( F ( A ) ) = ∑ j = 0 2 n − 1 d i , j F j ( A ) = ∑ j = 0 2 n − 1 d i , j ( ∑ k = 0 2 n − 1 c j , k A k ) = ∑ k = 0 2 n − 1 A k ( ∑ j = 0 2 n − 1 d i , j c j , k ) \begin{aligned}
\mathcal G_i(\mathcal F(A))&=\sum_{j=0}^{2^n-1} d_{i,j}\mathcal F_j(A)\\
&=\sum_{j=0}^{2^n-1} d_{i,j}\left(\sum_{k=0}^{2^n-1} c_{j,k} A_k\right)\\
&=\sum_{k=0}^{2^n-1} A_k\left(\sum_{j=0}^{2^n-1} d_{i,j}c_{j,k}\right)
\end{aligned}
G i ( F ( A )) = j = 0 ∑ 2 n − 1 d i , j F j ( A ) = j = 0 ∑ 2 n − 1 d i , j ( k = 0 ∑ 2 n − 1 c j , k A k ) = k = 0 ∑ 2 n − 1 A k ( j = 0 ∑ 2 n − 1 d i , j c j , k )
于是我们希望 ∑ j = 0 2 n − 1 d i , j c j , k = [ i = k ] \sum_{j=0}^{2^n-1} d_{i,j}c_{j,k}=[i=k] ∑ j = 0 2 n − 1 d i , j c j , k = [ i = k ] ,这显然是一个矩阵的形式,于是这个式子成立的充要条件是 d d d 的矩阵与 c c c 的矩阵之积是单位阵 I I I 。
进一步地,如果下面的式子成立:
[ c 0 , 0 c 0 , 1 c 1 , 0 c 1 , 1 ] [ d 0 , 0 d 0 , 1 d 1 , 0 d 1 , 1 ] = I 2 \begin{bmatrix}c_{0,0}&c_{0,1}\\c_{1,0}&c_{1,1}\end{bmatrix} \begin{bmatrix}d_{0,0}&d_{0,1}\\d_{1,0}&d_{1,1}\end{bmatrix}=I_2
[ c 0 , 0 c 1 , 0 c 0 , 1 c 1 , 1 ] [ d 0 , 0 d 1 , 0 d 0 , 1 d 1 , 1 ] = I 2
令左边的矩阵是 F F F ,右边的矩阵是 G G G ,就会有:
[ c 0 , 0 c 0 , 1 c 0 , 2 c 0 , 3 c 1 , 0 c 1 , 1 c 1 , 2 c 1 , 3 c 2 , 0 c 2 , 1 c 2 , 2 c 2 , 3 c 3 , 0 c 3 , 1 c 3 , 2 c 3 , 3 ] = [ c 0 , 0 F c 0 , 1 F c 1 , 0 F c 1 , 1 F ] , [ d 0 , 0 d 0 , 1 d 0 , 2 d 0 , 3 d 1 , 0 d 1 , 1 d 1 , 2 d 1 , 3 d 2 , 0 d 2 , 1 d 2 , 2 d 2 , 3 d 3 , 0 d 3 , 1 d 3 , 2 d 3 , 3 ] = [ d 0 , 0 G d 0 , 1 G d 1 , 0 G d 1 , 1 G ] \begin{bmatrix}
c_{0,0}&c_{0,1}&c_{0,2}&c_{0,3}\\
c_{1,0}&c_{1,1}&c_{1,2}&c_{1,3}\\
c_{2,0}&c_{2,1}&c_{2,2}&c_{2,3}\\
c_{3,0}&c_{3,1}&c_{3,2}&c_{3,3}\\
\end{bmatrix}=
\begin{bmatrix}
c_{0,0} F&c_{0,1}F\\
c_{1,0} F&c_{1,1}F
\end{bmatrix},
\begin{bmatrix}
d_{0,0}&d_{0,1}&d_{0,2}&d_{0,3}\\
d_{1,0}&d_{1,1}&d_{1,2}&d_{1,3}\\
d_{2,0}&d_{2,1}&d_{2,2}&d_{2,3}\\
d_{3,0}&d_{3,1}&d_{3,2}&d_{3,3}\\
\end{bmatrix}=
\begin{bmatrix}
d_{0,0} G&d_{0,1}G\\
d_{1,0} G&d_{1,1}G
\end{bmatrix}
c 0 , 0 c 1 , 0 c 2 , 0 c 3 , 0 c 0 , 1 c 1 , 1 c 2 , 1 c 3 , 1 c 0 , 2 c 1 , 2 c 2 , 2 c 3 , 2 c 0 , 3 c 1 , 3 c 2 , 3 c 3 , 3 = [ c 0 , 0 F c 1 , 0 F c 0 , 1 F c 1 , 1 F ] , d 0 , 0 d 1 , 0 d 2 , 0 d 3 , 0 d 0 , 1 d 1 , 1 d 2 , 1 d 3 , 1 d 0 , 2 d 1 , 2 d 2 , 2 d 3 , 2 d 0 , 3 d 1 , 3 d 2 , 3 d 3 , 3 = [ d 0 , 0 G d 1 , 0 G d 0 , 1 G d 1 , 1 G ]
于是,这两个矩阵按照分块矩阵的乘法乘起来也是单位阵 I 4 I_4 I 4 ,以此类推,最大的那个 F , G F,G F , G 乘起来就是 I 2 n I_{2^n} I 2 n 。
注意,由 2 × 2 2\times 2 2 × 2 到 4 × 4 4\times 4 4 × 4 的过程中,以 c 0 , 0 c_{0,0} c 0 , 0 为例,它其实发生了一个微妙的变化:原来 2 × 2 2\times 2 2 × 2 的矩阵中,这个 0 / 1 0/1 0/1 是长度为 1 1 1 的二进制数,扩张之后是长度为 2 2 2 的二进制数。这在当前讨论的与或异或卷积的体现不是很明显,但是在下面的一般化的公式中这就有区别了。
下面是洛谷模板题的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 void OR (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) { for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { A[i+j+half] = (A[i+j+half] + A[i+j]) % P; } } } } void IOR (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) { for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { A[i+j+half] = (A[i+j+half] - A[i+j] + P) % P; } } } } void AND (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) { for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { A[i+j] = (A[i+j] + A[i+j+half]) % P; } } } } void IAND (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) { for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { A[i+j] = (A[i+j] - A[i+j+half] + P) % P; } } } } void XOR (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) for (int i = 0 ; i < 1 << n; i += len) for (int j = 0 ; j < half; j++) { int A0 = A[i+j], A1 = A[i+j+half]; A[i+j] = (A0 + A1) % P; A[i+j+half] = (A0 - A1) % P; if (A[i+j+half] < 0 ) A[i+j+half] += P; } } void IXOR (int A[], int n) { for (int len = 2 , half = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 ) for (int i = 0 ; i < 1 << n; i += len) for (int j = 0 ; j < half; j++) { int A0 = A[i+j], A1 = A[i+j+half]; A[i+j] = 1ll * (A0 + A1) * INV2 % P; A[i+j+half] = 1ll * (A0 - A1) * INV2 % P; if (A[i+j+half] < 0 ) A[i+j+half] += P; } }
一般化
这里给出一道题目:Belarusian State University 。
对于任意给定真值表的二进制位独立的运算 ∘ \circ ∘ ,我们还能导出上面的结论吗?答案是否定的。
例如,如果 j ∘ k ≡ 1 j\circ k\equiv 1 j ∘ k ≡ 1 (恒等于 1 1 1 ),那么可以推出 c i , j ≡ 1 c_{i,j}\equiv 1 c i , j ≡ 1 ,此时的矩阵显然就是不可逆的了。
此时我们退而求其次,可以尝试找三种变换 F , G , H \mathcal F,\mathcal G,\mathcal H F , G , H ,满足:
H ( A ∗ B ) = F ( A ) ⋅ G ( B ) \mathcal H(A*B)=\mathcal F(A)\cdot \mathcal G(B)
H ( A ∗ B ) = F ( A ) ⋅ G ( B )
其中 H \mathcal H H 是可逆的变换,F , G \mathcal F,\mathcal G F , G 可以不可逆。
设 H \mathcal H H 对应的系数是 h 0 / 1 , 0 / 1 h_{0/1,0/1} h 0/1 , 0/1 ,由上一节的推导同理可得 h i , j ∘ k = f i , j g i , k h_{i,j\circ k}=f_{i,j}g_{i,k} h i , j ∘ k = f i , j g i , k 。
由于运算一共有 2 4 2^4 2 4 种,然后 f , g , h f,g,h f , g , h 的取值一共有 ( 3 4 ) 3 (3^4)^3 ( 3 4 ) 3 种,这是可以暴力寻找的。
下面是打表程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> using namespace std;int pow3[5 ] = { 1 , 3 , 9 , 27 , 81 , }; int calc (int rule, int a, int b) { return rule >> ((a << 1 ) | b) & 1 ; } bool validate (int rule, int f, int g, int h) { for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 2 ; j++) { for (int k = 0 ; k < 2 ; k++) { int ij = (i << 1 ) | j; int ik = (i << 1 ) | k; int ijk = (i << 1 ) | calc (rule, j, k); if ( (f / pow3[ij] % 3 - 1 ) * (g / pow3[ik] % 3 - 1 ) != (h / pow3[ijk] % 3 - 1 ) ) { return false ; } } } } return true ; } array<tuple<int ,int ,int >, 16> init () { array<tuple<int ,int ,int >, 16> res{}; for (int rule = 0 ; rule < 16 ; rule++) { for (int h = 0 ; h < pow3[4 ]; h++) { int h00 = h % 3 - 1 ; int h01 = h / 3 % 3 - 1 ; int h10 = h / 9 % 3 - 1 ; int h11 = h / 27 % 3 - 1 ; if (h00 * h11 - h01 * h10 == 0 ) continue ; for (int f = 0 ; f < pow3[4 ]; f++) { for (int g = 0 ; g < pow3[4 ]; g++) { if (validate (rule, f, g, h)) { res[rule] = {f, g, h}; } } } } } return res; } signed main () { ios::sync_with_stdio (false ); cin.tie (0 ); cout.tie (0 ); auto tab = init (); for (auto [f, g, h]: tab) { cout << '{' << f << ',' << g << ',' << h << "}," ; } return 0 ; }
用这个打出来的表,就可以来做自定义运算的 FWT 了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <bits/stdc++.h> using namespace std;#define int long long tuple<int ,int ,int > fgh[] = {{80 ,76 ,79 },{77 ,77 ,79 },{77 ,79 ,79 },{77 ,80 ,79 },{79 ,77 ,79 },{80 ,77 ,79 },{78 ,74 ,78 },{79 ,79 ,77 },{79 ,79 ,79 },{78 ,78 ,78 },{80 ,79 ,79 },{79 ,77 ,77 },{79 ,80 ,79 },{77 ,79 ,77 },{77 ,77 ,77 },{80 ,80 ,79 }}; int rule[20 ];int A[1 << 18 ], B[1 << 18 ];template <size_t F>void FWT (int n, int A[]) { for (int len = 2 , half = 1 , k = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 , k++) { int f = get <F>(fgh[rule[k]]); int f00 = f % 3 - 1 ; int f01 = f / 3 % 3 - 1 ; int f10 = f / 9 % 3 - 1 ; int f11 = f / 27 % 3 - 1 ; for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { int A0 = A[i+j], A1 = A[i+j+half]; A[i+j] = f00 * A0 + f01 * A1; A[i+j+half] = f10 * A0 + f11 * A1; } } } } void IFWT (int n, int A[]) { for (int len = 2 , half = 1 , k = 1 ; len <= 1 << n; len <<= 1 , half <<= 1 , k++) { int h = get <2 >(fgh[rule[k]]); int h00 = h % 3 - 1 ; int h01 = h / 3 % 3 - 1 ; int h10 = h / 9 % 3 - 1 ; int h11 = h / 27 % 3 - 1 ; int div = h00 * h11 - h01 * h10; int r00 = h11, r01 = -h01, r10 = -h10, r11 = h00; for (int i = 0 ; i < 1 << n; i += len) { for (int j = 0 ; j < half; j++) { int A0 = A[i+j], A1 = A[i+j+half]; A[i+j] = r00 * A0 + r01 * A1; A[i+j+half] = r10 * A0 + r11 * A1; assert (A[i+j] % div == 0 ); A[i+j] /= div; assert (A[i+j+half] % div == 0 ); A[i+j+half] /= div; } } } } signed main () { ios::sync_with_stdio (false ); cin.tie (0 ); cout.tie (0 ); int n; cin >> n; for (int k = 1 ; k <= n; k++) { string s; cin >> s; for (int i = 0 ; i < 2 ; i++) { for (int j = 0 ; j < 2 ; j++) { rule[k] ^= (s[(i << 1 ) | j] - '0' ) << ((i << 1 ) | j); } } } for (int i = 0 ; i < 1 << n; i++) cin >> A[i]; FWT <0 >(n, A); for (int i = 0 ; i < 1 << n; i++) cin >> B[i]; FWT <1 >(n, B); for (int i = 0 ; i < 1 << n; i++) A[i] = A[i] * B[i]; IFWT (n, A); for (int i = 0 ; i < 1 << n; i++) { cout << A[i] << " \n" [i == (1 << n) - 1 ]; } return 0 ; }