算是个小知识点吧
对于常见的前缀和,正常人知道的求法是通过容斥
(为了方便表示就用了 1 开始的下标和伪代码)
loop i:[1,x] loop j:[1,y] loop k:[1,z]
sum[i][j][k] = arr[i][j][k]
+ sum[i-1][j][k] + sum[i][j-1][k] + sum[i][j][k-1]
- sum[i-1][j-1][k] - sum[i-1][j][k-1] - sum[i][j-1][k-1]
+ sum[i-1][j-1][k-1]
其实还有更低复杂度的写法
loop i:[1,x] loop j:[1,y] loop k:[1,z] arr[i][j][k] += arr[i][j][k-1]
loop i:[1,x] loop j:[1,y] loop k:[1,z] arr[i][j][k] += arr[i][j-1][k]
loop i:[1,x] loop j:[1,y] loop k:[1,z] arr[i][j][k] += arr[i-1][j][k]
而高维前缀和是对于二进制的\(n\)个维度\(arr[2][2][2][2][2]...\),求出\(sum[2][2][2][2][2]...\)
(换成\(n\)个数的\(arr[1,n]\)数组就是求所有子集的和)
就刚才第二种的原地求和算法,可以模拟出从低位到高位维度的求和过程
(为了方便枚举位就用了 0 开始的下标)
for(int j = 0; (1<<j) < n; j++)
for(int i = 0; i < n; i++)
if(i>>j&1) arr[i] += arr[i^(1<<j)];
// 因为每一维就只有 0 和 1,因此对于 i 的第 j 位 xor 一下就是 1 变为 0
(这 tm 就是状压 DP 的最简单形式。。)