[学习笔记] 高维前缀和

算法用途

\[\sum_{i = 0}^{n} [i\ \&\ n = i] a_i \]

(实际上, 这只是高维前缀和的一种特殊形式, 即每一维的大小都为 2.)


算法过程

我们计算矩阵前缀和时, 通常用的是容斥的方法.

设当前要计算 \(D\) 维前缀和, 容斥的复杂度为

\[\sum_{i = 0}^n \binom{D}{i} = 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\), 所以你把中间那个循环的顺序反过来也行, 而更一般的情况下就要严格按照顺序转移了.


有什么没看懂或者表达不严谨的地方欢迎留言

posted @ 2020-07-27 22:26  BruceW  阅读(421)  评论(0编辑  收藏  举报