「学习笔记」FWT
「学习笔记」FWT
点击查看目录
理论基础
引入 \(\newcommand{FWT}[1]{\operatorname{FWT}(#1)}\)
常见的多项式卷积形式是 \(C[i] = \sum\limits_{j + k = i}A[j]B[k]\) .
类似地定义位运算卷积是形如 \(C[i] = \sum\limits_{j\oplus k = i}A[j]B[k]\) 的卷积,其中 \(\oplus\) 是一种位运算 .
FWT 可以以 \(O(n2^n)\) 的时间复杂度处理长度为 \(2^n\) 的位运算卷积 .
其过程同样可以类比 FFT,找到一种点值的映射后转化为点积 .
点值的构造
令 \(\FWT{A}\) 表示 \(A\) 经过变换后得到的幂级数 .
用 \(c(i, j)\) 表示系数,一个线性变换可以被表示为如下形式:
现在所期望做的是构造出合适的 \(c(i, j)\) 满足点积运算的正确性 . 对于不同的位运算应有不同的构造方式,不过在此之前应该先将 \(c\) 的性质挖掘一些 .
首先展开点积式:
代入位运算卷积的定义,应有以下等式:
容易注意到当 \(c(i, j\oplus k) = c(i, j)c(i, k)\) 时等式一定成立,构造可以利用这个性质 .
另外注意到 \(\oplus\) 是位运算,那么总存在 \(j\oplus k = t\iff j_i\oplus k_i = t_i\)(\(x_i\) 表示 \(x\) 二进制下的第 \(i\) 位),这提醒我们可以增加 \(c\) 的限制使得其可以拆位考虑进一步方便构造 . 一个简单的方式是钦定 \(c(i, j) = \prod c(i_k, j_k)\),这使得仅需要构造 \(i, j\in\{0, 1\}\) 时的 \(c(i, j)\) 即可得到所有 \(c(i, j)\) .
FWT 变换
现在的问题是,如何快速求出所有的 \(\FWT{A}[i]\),即 \(\sum_{j = 0}^{2^n - 1} c(i, j) A[j]\) .
分治考虑,可以按照 \(j\) 的最高位分为两部分,即:
然后直接类似于 FFT 的方式枚举当前处理的长度 \(2^k\) 倍增处理即可.
具体的,假设当前已经求出了 \(A\) 左右两个子部分 \(A_0, A_1\) 的变换,那么有:
不同位运算转移矩阵的构造
直接根据上文的性质对 \(2\times 2\) 矩阵列方程即可 .
不过注意我们还需定义 FWT 的逆变换,所以需要保证矩阵的逆存在 .
Or 卷积
容易得到每行三组解,不过因为需要保证逆的存在仅有两组解:\(\begin{bmatrix}1&\ 1\\1&\ 0\end{bmatrix}\) 和 \(\begin{bmatrix}1&\ 0\\1&\ 1\end{bmatrix}\) .
这里选用第二组解,其逆为 \(\begin{bmatrix}1\ &0\\-1\ &1\end{bmatrix}\) .
And 卷积
保证逆的存在有与 or 相似的两组解:\(\begin{bmatrix}1&\ 1\\0&\ 1\end{bmatrix}\) 和 \(\begin{bmatrix}0&\ 1\\1&\ 1\end{bmatrix}\) .
这里选用第一组解,其逆为 \(\begin{bmatrix}1&\ -1\\0&\ 1\end{bmatrix}\) .
Xor 卷积
保证逆的存在的两组解:\(\begin{bmatrix}1&\ 1\\1&\ -1\end{bmatrix}\) 和 \(\begin{bmatrix}1&\ -1\\1&\ 1\end{bmatrix}\) .
这里选用第一组解,其逆为 \(\begin{bmatrix}\dfrac{1}{2}&\ \dfrac{1}{2}\\\dfrac{1}{2}&\ -\dfrac{1}{2}\end{bmatrix}\) .
代码
namespace _FWT_ {
const int inv2 = (P + 1) / 2;
typedef std::array <std::array <int, 2>, 2> CMat;
const CMat OR ({ 1, 0, 1, 1 }), IOR ({ 1, 0, P - 1, 1 });
const CMat AND ({ 1, 1, 0, 1 }), IAND ({ 1, P - 1, 0, 1 });
const CMat XOR ({ 1, 1, 1, P - 1 }), IXOR ({ inv2, inv2, inv2, P - inv2 });
void FWT (int* F, int n, const CMat& C) {
for (int len = 1; len < n; len <<= 1) {
for (int p = 0; p < n; p += len * 2) {
_for (i, p, p + len - 1) {
ll w0 = F[i], w1 = F[i + len];
F[i] = (C[0][0] * w0 + C[0][1] * w1) % P;
F[i + len] = (C[1][0] * w0 + C[1][1] * w1) % P;
}
}
}
return;
}
}
例题
线性基
私题放不出 .
Takahashi The Strongest
首先平移一下胜利条件变为三个值相等 . 使用三进制描述策略,题目所求的相当于进行一个卷积:
不过这样并没有办法定义位运算,因为 \(j, k\) 确定时结果却不确定 . 考虑设一个中间状态 \(0\) 表示可以这一位不限制输赢可以任意取,这使得原来的卷积转化为点积,和中间状态相关的转移可以使用 FWT(贡献累加到 \(0\) 上)和 IFWT(\(0\) 贡献出去)的方式做 .
另外注意到不限制输赢的方案会导致算重,需要做容斥 .

浙公网安备 33010602011771号