算法用途
求
(实际上, 这只是高维前缀和的一种特殊形式, 即每一维的大小都为 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), 所以你把中间那个循环的顺序反过来也行, 而更一般的情况下就要严格按照顺序转移了.
有什么没看懂或者表达不严谨的地方欢迎留言