[学习笔记] 高维前缀和
算法用途
求
(实际上, 这只是高维前缀和的一种特殊形式, 即每一维的大小都为 2.)
算法过程
我们计算矩阵前缀和时, 通常用的是容斥的方法.
设当前要计算 \(D\) 维前缀和, 容斥的复杂度为
当维数过大时显然不行.
我们考虑计算矩阵前缀和的另一种方法 : 先算出每一列的前缀和, 然后再把每一列的前缀和加起来即可得到一个矩阵的前缀和.
我们来分析一下这个方法的实质 : 矩阵是一个二维结构, 我们用二元组 \((i,j)\) 来表示矩阵上的点. 那么每一列的前缀和实质上是控制了 \(j\) 这一维后计算的前缀和, 而一个矩阵的前缀和就是 \(i,j\) 这两维都不进行控制的前缀和.
考虑把这个方法推广到 \(D\) 维 : 先求出控制 前 \(D\) 维 的前缀和, 再求出控制 前 \(D - 1\) 维 的前缀和, 再求出控制 前 \(D - 2\) 维的前缀和 ...... 最终求出控制 前 \(0\) 维 的前缀和, 即为 \(D\) 维前缀和.
用式子来表示就是 : 设 \(sum[i][s]\) 为控制了 前 \(D - i\) 维的点 \(s\) 的前缀和 (即 \(sum[i][s]\) 内统计到的点的 前 \(D - i\) 维 都和 \(s\) 相同), 那么 \(sum[i][s] = sum[i - 1][s] + sum[i][s']\), 其中 \(s'\) 为前 \(i - 1\) 维都与 \(s\) 相同 且 第 \(i\) 维比 \(s\) 小 \(1\) 的点. 联想一下矩阵前缀和就很好理解了.
代码实现
由于在转移过程中, \(s'\) 严格小于 \(s\), 所以 \(sum\) 的第一维可以用滚动数组优化.
这里只给出了每一维的大小都等于 \(2\) 的情况的代码, 即「算法用途」中给出的形式.
for (int i = 0; i < n; i++)
for (int s = 0; s < 1 << n; s++)
if (s >> i & 1) sum[s] += sum[s ^ (1 << i)];
当然, 由于这里每一维的大小都为 \(2\), 所以你把中间那个循环的顺序反过来也行, 而更一般的情况下就要严格按照顺序转移了.
有什么没看懂或者表达不严谨的地方欢迎留言