高维前缀和小记
问题模型
设有一个D维空间,每一维大小都为N,对每个位置\((a_1,a_2,\cdots,a_D)\)求出\(s(a_1,a_2,\cdots,a_D)=\sum_{k_1=0}^{a_1}\sum_{k_2=0}^{a_2}\cdots \sum_{k_D=0}^{a_D} w(k_1,k_2,\cdots,k_D)\)
一般来说,在D较大时,题目中的N通常为2,因此,下面的分析,会特别注意2的情况
低维做法
- 暴力
直接按照式子计算,复杂度\(O(N^{2D})\),分析方法: 所求即为\((\sum_{i=1}^Ni)^D\)
当\(N=2\)时,复杂度\(O(3^D)\) - 容斥
低于3维时,求前缀和通常会用容斥方法,假设我们求\(s(state)\),设\(S_i\)表示满足第\(i\)位小于等于\(state_i-1\),其他位\(j\)小于等于\(state_j\)的\(state'\)集合
那么
\(\begin{aligned} s(state)&=s(\bigcup_{i=1}^DS_i)+w(state)\\&= \sum_{m=1}^D(-1)^{m-1}\sum_{a_i<a_{i+1}}s(\bigcap_{i=1}^mS_{a_i}) \end{aligned}\)
,复杂度:\(O((2N)^D)\)
当\(N=2\)时,用二项式定理更精细分析一下,可得复杂度为\(O(3^D)\),与暴力相同 - 递推
先观察D较小的情况:
D=1:
for(int i=1;i<=n;i++)
a[i]+=a[i-1];
D=2:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]+=a[i][j-1];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]+=a[i-1][j];
D=3:
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=p;k++)
a[i][j][k]+=a[i-1][j][k];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=p;k++)
a[i][j][k]+=a[i][j-1][k];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=p;k++)
a[i][j][k]+=a[i][j][k-1];
通过观察可以得到这种方法的复杂度是\(O(D\times N^D)\)
当N=2时,复杂度为\(O(D\times 2^D)\)
代码实现类比低维即可:
for(int i=0;i<D;i++){
for(int j=0;j<(1<<D);j++){
if(j&(1<<i)) f[j]+=f[j^(1<<i)];
}
}
应用
- 与容斥好像很搭
例题:CF1620G
先用容斥转化,发现式子可以写成一个每一维大小为2的n维前缀和,套用刚才的做法即可。 - 优化狄利克雷卷积
对每个1~n求出\(f_n=\sum_{d|n}\mu(d)g(\frac{n}{d})\),即\(f=g*\mu\),用倍数法为\(O(Nlog N)\).
但是如果把\(\mu\)函数看做容斥系数的话,实际上f就是g的高维前缀差分数组,做高维前缀差分复杂度即为\(O(n\log \log n)\)
再比如如果\(f=g*I\),就相当于做高维前缀和(称\(f\)是\(g\)的狄利克雷前缀和)
\(f=g*id\),即\(f(x)=\sum_\limits{d|x}g(d)x/d=x\sum_\limits{d|x}g(d)/d\),那么对\(g(d)/d\)做狄利克雷前缀和,在对每个\(f\)乘上\(x\)即可。
除此之外还有一种求和形式,并不是卷积形式,而是类似于莫反的倍数形式,此时可以把求和看做高维后缀和,高维后缀差分(莫反倍数形式的本质),跟上面同样的做法。

浙公网安备 33010602011771号