【高维前缀和与位运算】学习笔记
一、高维前缀和
常见形式是类似多维数组的形式,求法是逐维递推,设维度为
另一种形式是二进制集合形式,每一维的值域都是
二、FMT(快速莫比乌斯变换)与应用——与或卷积
FMT 的主要变换形式为在二进制集合下进行高维前缀、后缀和,分别用来求解或、与卷积。
1. 或变换
正变换式子:
inline void Fwt_or(ll* f, int opt) {
for (int len = 2, k = 1; len <= U; len <<= 1, k <<= 1)
for (int i = 0; i < U; i += len)
for (int j = 0; j < k; ++j)
f[i + j + k] = (f[i + j + k] + f[i + j] * opt + Mod) % Mod;
return ;
}
2. 与变换
与或变换类似,本质上是翻转了二进制位,及转变为高维后缀和与高维后缀差分。
inline void Fwt_and(ll* f, int opt) {
for (int len = 2, k = 1; len <= U; len <<= 1, k <<= 1)
for (int i = 0; i < U; i += len)
for (int j = 0; j < k; ++j)
f[i + j] = (f[i + j] + f[i + j + k] * opt + Mod) % Mod;
return ;
}
3. 应用
此处以或变换求或卷积为例,也可以求与卷积、
题面:求数组
算法思路:对
本质:通过线性变换将问题加在一种空间里满足简单点乘关系,再通过逆变换求原数组,构造这种线性变换是解决问题的关键。
三、子集卷积
问题形如:
解法思路:固定大小再做普通或卷积。具体地,令
则可以通过 FMT 求出每一层次的
cin >> n, U = (1 << n);
for (int i = 0; i < U; ++i)
cin >> A[popcount(i)][i];
for (int i = 0; i < U; ++i)
cin >> B[popcount(i)][i];
for (int i = 0; i <= n; ++i)
FMT(A[i], 1), FMT(B[i], 1);
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= i; ++j)
for (int S = 0; S < U; ++S)
C[i][S] = (C[i][S] + A[j][S] * B[i - j][S] % Mod) % Mod;
for (int i = 0; i <= n; ++i)
FMT(C[i], -1);
for (int i = 0; i < U; ++i)
cout << C[popcount(i)][i] << ' ';
子集卷积也可以半在线实现,常应用于动态规划。
四、FWT(快速沃尔什变换)与异或卷积
通过构造针对异或位运算的线性变换可以得到 FWT 的异或变换。
inline void Fwt_xor(ll* f, int opt) {
for (int len = 2, k = 1; len <= U; len <<= 1, k <<= 1)
for (int i = 0; i < U; i += len)
for (int j = 0; j < k; ++j)
f[i + j] = (f[i + j] + f[i + j + k]) % Mod, f[i + j + k] = (f[i + j] - (f[i + j + k] << 1) + (Mod << 1)) % Mod,
f[i + j] = f[i + j] * opt % Mod, f[i + j + k] = f[i + j + k] * opt % Mod;
return ;
}
正、逆变换的参数为
五、整体总结
FMT、高维前缀和、FWT、子集卷积都是难度较大的技巧,真正掌握需要深刻的理解!
六、例题
模板题如下:P4717 与 P6097,比较不错的题目目前有 CF449D。