FWT
FWT 快速沃尔什变换
一种以 \(O(n \times 2^n)\) 的时间复杂度快速计算位运算卷积的算法。
位运算卷积
分三种。
- OR 卷积(并集卷积)
正变换即 SOS dp,逆变换即逆向 SOS dp。
证明:
\[\text{FWT\_OR}(a)[k] \times \text{FWT\_OR}(b)[k] \\ = \sum_{s \subset k} a_s \sum_{t \subset k} b_t \\ = \sum_{s \subset k} \sum_{t \subset k} a_s \cdot b_t \\ = \sum_{s \cup t \subset k} a_s \cdot b_t \\ = \sum_{u \subset k} \sum_{s \cup t=u} a_s \cdot b_t \\ = \sum_{u \subset k} c_u \\ = \text{FWT\_OR}(c)[k] \]
- AND 卷积(交集卷积)
正变换即超集 SOS dp,逆变换即逆向超集 SOS dp,证明同上。
- XOR 卷积
这个东西不好直接 dp 求解,提供一种分治的思路。
设数组长度为 \(2^n\),考虑最高位的情况:
-
\(a_0\):下标 \(0 \sim 2^{n-1}-1\),对应最高位为 \(0\)
-
\(a_1\):下标 \(2^{n-1} \sim 2^{n}-1\),对应最高位为 \(1\)
对于 \(b,c\) 数组,我们同样做类似的划分。
以上是正变换。
考虑计算 \(c\):
-
对于 \(c_0\),显然 \(a,b\) 最高位相同,有 \(c_0 = a_0 * b_0 + a_1 * b_1\)(\(*\) 表示去掉最高位之后做卷积)
-
对于 \(c_1\),同理有 \(c_1 = a_0 * b_1 + a_1 * b_0\)。
这个东西不好直接点乘计算,考虑构造。
具体的,我们构造 \(A_0=a_0+a_1,A_1=a_0-a_1\)(\(b\) 同理),则有:
同理,\(A_1 B_1 = c_0 - c_1\),然后解方程即可得 \(c_0,c_1\)。
以上是逆变换。
例题选讲
- CF662C(简化问题的策略、紧扣所求)
我们发现行很小,考虑状压列,记为 \(j\)。
我们不妨先考虑行的反转操作,记行的操作为 \(mask\),则当前列的状态应为 \(j \oplus mask\)。对于这个状态,它最小的一的个数显然就是 \(\min(bit,n-bit)\)(\(bit\) 为该状态在二进制下 \(1\) 的个数),因为可以对列再翻转一次,将这个值记为 \(val_{j \oplus mask}\)。
然后问题转化为求 \(ans_{mask} = \sum val_{j \oplus mask} \cdot cnt_{j}\)(\(cnt_j\) 表示状态为 \(j\) 的列数),这不就是 XOR 卷积吗?直接 FWT 解决即可。
- P1539(OR 卷积优化状压 dp)
很容易直接枚举当前行和上一行的状态然后转移,这是 \(O(n \cdot 4^n)\) 的,显然无法通过。
然后我们发现当前行某一位填 \(0\) 时上一行随意,但是这一位填 \(1\) 时上一行就只能填 \(0\),说明上一行的状态是这一行补集的子集,这显然可以 OR 卷积(或者说 SOS dp)去优化之。
注意讨论一下 \(n,m\) 的大小关系,以便矩阵进行转置。
总结
FWT 一般用途:优化状压 dp、凑成特定形式快速计算答案。
浙公网安备 33010602011771号