闲话 23.1.13
闲话
计算几何怎么学?
今日推歌:《GravityRain》by てぃあら feat. 初音ミク
冷门良曲!很推荐听一听的!
昨天闲话怎么没人看啊??
引流!!!
\(\text{FWT}\)
发现这个知识点还没补,所以补上。
对照加卷积在幂级数上的贡献方式,我们定义位运算卷积对幂级数的贡献方式。假设 \(A,B\) 是两个幂级数,两者的位运算卷积满足 \(A[i]\times B[j]\) 对 \(i\oplus j\) 有贡献,其中 \(\oplus\) 为一种特定的位运算。形式化地,若 \(A, B\) 对位运算 \(\oplus\) 的卷积记作 \(A\oplus B\),则该运算产生的 \(S = A\oplus B\) 满足
对照 \(\text{DFT}\) 的做法,我们发现 \(\text{DFT}\) 把系数转成了点值后卷积操作变成了直接乘。我们也希望通过将系数转化后使位运算卷积变成乘法。
我们设 \(\text{FWT}(A)\) 是对幂级数 \(A\) 经过 \(\text{FWT}\) 变换后得到的幂级数,则我们需要满足
对照 \(\text{DFT}\) 的性质,我们希望 \(\text{FWT}\) 也是一个线性变换。不妨设 \(A[j]\) 对 \(\text{FWT}(A)[i]\) 的贡献系数为 \(c(i, j)\),我们能确定
我们考虑 \(\text{FWT}\) 的性质,得到
同时由于 \(A\oplus B = C\),有
对比系数得到 \(c(m, i)c(m, j) = c(m, i \oplus j)\)。发现 \(c\) 第二维可以由位运算决定,因此我们可以分位构造。具体地,我们可以得到 \(c(i, j)\) 对 \(i, j\in \{0, 1\}\) 的四种取值后计算每个 \(c(n, m)\)。记一个数字 \(a\) 二进制表示的第 \(i\) 位为 \(a_i\),则我们能得到
容易发现这对每一位是满足需要的,因此这式子肯定满足一种构造下的 \(c\) 函数。因此 \(\text{FWT}\) 的关键在于写出 \(c(i, j)\) 对 \(i, j\in \{0, 1\}\) 的四种取值,随后构造矩阵描述线性变换。
我们写出 \(\text{FWT}\) 的式子:
随后可以折半。我们设 \(i\) 去掉最高位得到 \(i'\),则有
可以发现这就是折半的过程。这启发我们通过低 \(n / 2\) 位和高 \(n / 2\) 位分别地描述 \(\text{FWT}\)。假设幂级数 \(A\) 的低 \(n/2\) 次项组成 \(A_0\),高 \(n/2\) 次项组成 \(A_1\),则我们可以发现,对于 \(i\in [0, n / 2)\) 有
这也就导出了所构造的矩阵。随后我们就能以 \(O(n)\) 的复杂度合并两个规模为 \(n/2\) 的子问题了。若 \(n = 2^k\),则总时间复杂度为 \(O(k2^k)\)。可以暴力模拟矩阵合并。
对应的 \(\text{IFWT}\) 也不难想象了,就是做矩阵求逆的过程。这隐式地要求下面的矩阵必须有逆,也就是满秩。
随后我们的问题就是根据不同的运算构造矩阵了。记要构造的矩阵
\(\text{or}\)
我们可以分别讨论:
- \(c(0, 0) c(0, 0) = c(0, 0|0) = c(0, 0)\)
\(c(0, 0) = 0/1\)。 - \(c(0, 1) c(0, 0) = c(0, 1|0) = c(0, 1)\)
\(c(0, 1) = 0\) 或 \(c(0, 0) = c(0, 1) = 1\)。 - \(c(1, 1) c(1, 0) = c(1, 1 | 0) = c(1, 1)\)
\(c(1, 1) = 0\) 或 \(c(1, 1) = c(1, 0) = 1\)。
我们可以得到两种矩阵:\(\begin{bmatrix} 1 & 1 \\ 1 & 0 \end{bmatrix}\) 和 \(\begin{bmatrix} 1 & 0 \\ 1 & 1 \end{bmatrix}\)。第二个矩阵和子集和是同构的,因此有时 SOSDP 会采用 \(\text{FWT}\) 优化合并。你问为啥?这是由于第二个矩阵描述了 \(c(i, j) = [i \text{ and } j = j]\),这在分位构造后就是 \(n\text{ and } m = m\),附一个状压意义就得到了 \(m \in n\)。
\(\text{and}\)
仍然是分别讨论:
- \(c(0, 1) c(0, 0) = c(0, 1\&0) = c(0, 0)\)
\(c(0, 0) = 0\) 或 \(c(0, 0) = c(0, 1) = 1\)。 - \(c(1, 1) c(1, 0) = c(1, 1 \& 0) = c(1, 0)\)
\(c(1, 0) = 0\) 或 \(c(1, 1) = c(1, 0) = 1\)。 - \(c(1, 1) c(1, 1) = c(1, 1 \& 1) = c(1, 1)\)
\(c(1, 1) = 0/1\)。
这也能构造出两种矩阵:\(\begin{bmatrix} 0 & 1 \\ 1 & 1 \end{bmatrix}\) 和 \(\begin{bmatrix} 1 & 1 \\ 0 & 1 \end{bmatrix}\)。和上面的是转置的结果。
\(\text{xor}\)
记异或为 \(\oplus\)。仍然是分别讨论:
- \(c(0, 0) c(i, j) = c(i, j\oplus 0) = c(i, j)\)
\(c(0, 0) = 1\)。 - \(c(1, 1) c(1, 1) = c(1, 1 \oplus 1) = c(1, 0)\)
这里 \(c(1, 1)\) 和 \(c(1, 0)\) 必须均非 \(0\)。 - \(c(1, 1) c(1, 0) = c(1, 1 \oplus 0) = c(1, 1)\)
\(c(1, 0) = 1\)。 - \(c(0, 1) c(0, 1) = c(0, 1 \oplus 1) = c(0, 0) = 1\)
\(c(0, 1) = 1 / -1\)。
这也能构造出两种矩阵:\(\begin{bmatrix} 1 & 1 \\ -1 & 1 \end{bmatrix}\) 和 \(\begin{bmatrix} 1 & 1 \\ 1 & -1 \end{bmatrix}\)。
这也是为什么对异或的 \(\text{IFWT}\) 需要除以 \(2\)。
直接通过矩阵乘法是可以实现的,cmd 的提交记录。
最后放出板子代码。可以看出,我们构造的 \(\text{FWT}\) 和 \(\text{FFT}\) 是基本同构的。
code
inline void OR(int f[], int len, int typ) {
if (typ != 1) typ = mod - 1;
for (int mid = 1; mid < len; mid <<= 1)
for (int i = 0, j = 0; j < len; j += (mid << 1))
for (int k = 0; k < mid; ++ k)
f[j + k + mid] = (f[j + k + mid] + 1ll * f[j + k] * typ) % mod;
}
inline void AND(int f[], int len, int typ) {
if (typ != 1) typ = mod - 1;
for (int mid = 1; mid < len; mid <<= 1)
for (int j = 0; j < len; j += (mid << 1))
for (int k = 0; k < mid; ++ k)
f[j + k] = (f[j + k] + 1ll * f[j + k + mid] * typ) % mod;
}
inline void XOR(int f[], int len, int typ) {
if (typ != 1) typ = (mod + 1) / 2;
for (int mid = 1, t; mid < len; mid <<= 1)
for (int j = 0; j < len; j += (mid << 1))
for (int k = 0; k < mid; ++ k)
t = f[j + k + mid],
f[j + k + mid] = 1ll * typ * (f[j + k] - f[j + k + mid] + mod) % mod,
f[j + k] = 1ll * typ * (f[j + k] + t) % mod;
}
同样可以直接构造并观察性质,但这在推导 \(\text{xor}\) 时会较麻烦。
以下是博客签名,与正文无关。
请按如下方式引用此页:
本文作者 joke3579,原文链接:https://www.cnblogs.com/joke3579/p/chitchat230113.html。
遵循 CC BY-NC-SA 4.0 协议。
请读者尽量不要在评论区发布与博客内文完全无关的评论,视情况可能删除。