FWT 学习笔记
\(\operatorname{FFT}\) 和 \(\operatorname{NTT}\) 都是在做加法卷积,从其形式 \(C_i = \sum\limits_{j = 0}^i A_j\times B_{i - j}\) 可以看出,其满足对应的下标 \(j + (i - j) = i\)。
考虑把这个 \(+\) 换成位运算 \(\operatorname{bitand}, \operatorname{bitor}, \operatorname{bitxor}\) 来卷积。
即令这个符号为 \(\oplus\),有 \(C_i = \sum\limits_{j \oplus k = i} A_j\times B_k(i, j, k\in [0, n), n = 2^m)\)。
考虑类似 \(\operatorname{FFT}\) 的过程:
\(A\stackrel{\operatorname{DFT}}{\longrightarrow} \operatorname{FFT}(A), B\stackrel{\operatorname{DFT}}{\longrightarrow} \operatorname{FFT}(B), \operatorname{FFT}(A)_i\times \operatorname{FFT}(B)_i \to \operatorname{FFT}(C)_i, \operatorname{FFT}(C)\stackrel{\operatorname{IDFT}}{\longrightarrow} C\)。
那能不能也这样通过 \(\operatorname{DWT}\) 得到 \(\operatorname{FWT}(A), \operatorname{FWT}(B)\),然后得到 \(\operatorname{FWT}(C)\),通过 \(\operatorname{IDWT}\) 得到 \(C\)。
考虑构造系数 \(c(i, j)\) 满足 \(\operatorname{FWT}(A)_i = c(i, j)\times A_j\)。
那么根据 \(\operatorname{FWT}(A)_i\times \operatorname{FWT}(B)_i = \operatorname{FWT}(C)_i\) 就有:
\((\sum\limits_{j} c(i, j)A_j)(\sum\limits_{k} c(i, j)B_k) = \sum\limits_{p} c(i, p) C_p\)
\(\sum\limits_{j}\sum\limits_{k} c(i, j)c(i, k)A_j B_k = \sum\limits_{p} c(i, p) C_p\)
考虑卷积的形式,有 \(C_p = \sum\limits_{p1\oplus p2 = p} A_{p1}\times B_{p2}\),就有:
\(\sum\limits_{p} C_p = \sum\limits_{p} \sum\limits_{p1\oplus p2 = p} A_{p1} B_{p2}\)
\(\sum\limits_{p} c(i, p) C_p = \sum\limits_{p} c(i, p)\sum\limits_{p1\oplus p2 = p} A_{p1} B_{p2}\)
\(\sum\limits_{p} c(i, p) C_p = \sum\limits_{p}\sum\limits_{p1\oplus p2 = p} c(i, p1\oplus p2) A_{p1} B_{p2}\)
\(\sum\limits_{p} c(i, p) C_p = \sum\limits_{p1}\sum\limits_{p2} c(i, p1\oplus p2) A_{p1} B_{p2}\)
把两个最终的式子放在一起,就有 \(\sum\limits_{j}\sum\limits_{k} c(i, j)c(i, k)A_j B_k = \sum\limits_{p1}\sum\limits_{p2} c(i, p1\oplus p2) A_{p1} B_{p2}\)。
就能得到 \(c(i, j)c(i, k) = c(i, j\oplus k)\)。
又根据位运算每一位是独立的这一性质,考虑把 \(c(i, j)\) 摊到 \((i, j)\) 的每一位上。
即令 \(i_{p}\) 为 \(i\) 二进制下的第 \(p\) 高位(\(p = 0\) 为最高位),考虑得到了 \(c(i, j) (i, j\in [0, 1])\),有 \(c(i, j) = \prod\limits_{p} c(i_p, j_p)\)。
只要有 \(c(i_p, j_p) c(i_p, k_p) = c(i_p, j_p\oplus k_p)\),那么就有 \(c(i, j) c(i, k) = c(i, j\oplus k)\),通过拆位证明。
如果知道了 \(c(i, j)\),又如何进行 \(\operatorname{DWT}\) 呢?
考虑 \(\operatorname{FWT}(A)_i = \sum\limits_{j}^{n - 1} c(i, j) A_j\),那么对于最高位,在 \(0\sim \frac{n}{2} - 1\) 时为 \(0\),\(\frac{n}{2}\sim n - 1\) 时为 \(0\)。
令 \(i', j'\) 分别为 \(i, j\) 舍弃最高位后的数,那就有 \(\operatorname{FWT}(A)_i = \sum\limits_{j = 0}^{\frac{n}{2} - 1} c(i_0, 0) c(i', j') A_j + \sum\limits_{j = \frac{n}{2}}^{n - 1} c(i_0, 1) c(i', j') A_j = c(i_0, 0) \sum\limits_{j = 0}^{\frac{n}{2} - 1} c(i', j') A_j + c(i_0, 1)\sum\limits_{j = \frac{n}{2}}^{n - 1} c(i', j') A_j\)。
发现这也是可以分治的,考虑 \(A'_0\) 为首位为 \(0\) 的 \(A_i\) 得到的序列,\(A'_1\) 同理。
那就有 \(\operatorname{FWT}(A)_i= c(i_0, 0) \operatorname{FWT}(A'_0)_{i'} + c(i_0, 1)\operatorname{FWT}(A'_1)_{i'}\)。
再分讨一下 \(i_0\) 的值,就有 \(\operatorname{FWT}(A)_i = c(0, 0) \operatorname{FWT}(A'_0)_{i} + c(0, 1)\operatorname{FWT}(A'_1)_{i}, \operatorname{FWT}(A)_{i + \frac{n}{2}} = c(1, 0) \operatorname{FWT}(A'_0)_{i} + c(1, 1)\operatorname{FWT}(A'_1)_{i}(i\in [0, \frac{n}{2}))\)。
按照这个系数直接分治就行了。
因为只会往下分治 \(\log n\) 层,每一层都会重新计算 \(\operatorname{FWT}(A)_i\),时间复杂度 \(O(n\log n)\)。
考虑把 \(c(i, j)(i, j\in [0, 1])\) 写为一个 \(2\times 2\) 的矩阵。
那么 \(\text{IDWT}\) 只需要把 \(\text{DWT}\) 的 \(c\) 替换为其逆矩阵即 \(c^{-1}\) 即可。
\(\operatorname{bitor}\) 卷积:
需要满足 \(c(i, j)c(i, k) = c(i, j\operatorname{bitor} k)\)。
就有 \(c(i, 0)c(i, 0) = c(i, 0), c(i, 0)c(i, 1) = c(i, 1), c(i, 1)c(i, 1) = c(i, 1)\)。
可以构造出 \(c = \begin{bmatrix}1 & 1\\ 1 & 0\end{bmatrix}, c^{-1} = \begin{bmatrix}0 & 1 \\ 1 & -1\end{bmatrix}\)。
\(\operatorname{bitand}\) 卷积:
需要满足 \(c(i, j)c(i, k) = c(i, j\operatorname{bitand} k)\)。
就有 \(c(i, 0)c(i, 0) = c(i, 0), c(i, 0)c(i, 1) = c(i, 0), c(i, 1)c(i, 1) = c(i, 1)\)。
可以构造出 \(c = \begin{bmatrix}1 & 1 \\ 0 & 1\end{bmatrix}, c^{-1} = \begin{bmatrix}1 & -1 \\ 0 & 1\end{bmatrix}\)。
\(\operatorname{bitxor}\) 卷积:
需要满足 \(c(i, j)c(i, k) = c(i, j\operatorname{bitxor} k)\)。
就有 \(c(i, 0)c(i, 0) = c(i, 0), c(i, 0)c(i, 1) = c(i, 1), c(i, 1)c(i, 1) = c(i, 0)\)。
可以构造出 \(c = \begin{bmatrix}1 & 1 \\ 1 & -1\end{bmatrix}, c^{-1} = \begin{bmatrix}\frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & -\frac{1}{2}\end{bmatrix}\)。
模板题 Luogu P4717。
直接套用上面的做法即可。
可以直接从低位到高位递推减小常数。
此题 \(n\) 为位数,时间复杂度就为 \(O(n2^n)\)。
#include<bits/stdc++.h>
using i64 = long long;
const i64 mod = 998244353, inv2 = (mod + 1) >> 1;
const int maxn = (1 << 17) + 2;
namespace FWT {
const i64 Bitor[2][2] = {{1, 1}, {1, 0}}, IBitor[2][2] = {{0, 1}, {1, mod - 1}};
const i64 Bitand[2][2] = {{1, 1}, {0, 1}}, IBitand[2][2] = {{1, mod - 1}, {0, 1}};
const i64 Bitxor[2][2] = {{1, 1}, {1, mod - 1}}, IBitxor[2][2] = {{inv2, inv2}, {inv2, mod - inv2}};
inline void FWT(i64 *a, const i64 B[2][2], int n) {
for (int i = 0; (1 << i) < n; i++)
for (int s = 0; s < n; s += 2 << i)
for (int p = s, ed = s + (1 << i); p < ed; p++) {
i64 x = a[p], y = a[p + (1 << i)];
a[p] = (B[0][0] * x + B[0][1] * y) % mod, a[p + (1 << i)] = (B[1][0] * x + B[1][1] * y) % mod;
}
}
};
i64 a[maxn], b[maxn], a_[maxn], b_[maxn];
int main() {
int n; scanf("%d", &n);
n = 1 << n;
for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
for (int i = 0; i < n; i++) scanf("%lld", &b[i]);
auto solve = [&](int id) {
for (int i = 0; i < n; i++) a_[i] = a[i], b_[i] = b[i];
if (id == 1) FWT::FWT(a_, FWT::Bitor, n), FWT::FWT(b_, FWT::Bitor, n);
if (id == 2) FWT::FWT(a_, FWT::Bitand, n), FWT::FWT(b_, FWT::Bitand, n);
if (id == 3) FWT::FWT(a_, FWT::Bitxor, n), FWT::FWT(b_, FWT::Bitxor, n);
for (int i = 0; i < n; i++) (a_[i] *= b_[i]) %= mod;
if (id == 1) FWT::FWT(a_, FWT::IBitor, n);
if (id == 2) FWT::FWT(a_, FWT::IBitand, n);
if (id == 3) FWT::FWT(a_, FWT::IBitxor, n);
for (int i = 0; i < n; i++) printf("%lld ", a_[i]); printf("\n");
};
solve(1),solve(2), solve(3);
return 0;
}

浙公网安备 33010602011771号