高维前缀和小记

问题模型

设有一个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的情况

低维做法

  1. 暴力
    直接按照式子计算,复杂度\(O(N^{2D})\),分析方法: 所求即为\((\sum_{i=1}^Ni)^D\)
    \(N=2\)时,复杂度\(O(3^D)\)
  2. 容斥
    低于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)\),与暴力相同
  3. 递推

先观察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)]; 
    }
}

应用

  1. 与容斥好像很搭
    例题:CF1620G
    先用容斥转化,发现式子可以写成一个每一维大小为2的n维前缀和,套用刚才的做法即可。
  2. 优化狄利克雷卷积
    对每个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\)即可。

除此之外还有一种求和形式,并不是卷积形式,而是类似于莫反的倍数形式,此时可以把求和看做高维后缀和,高维后缀差分(莫反倍数形式的本质),跟上面同样的做法。

posted @ 2022-07-29 10:12  glq_C  阅读(61)  评论(0)    收藏  举报