前缀和与差分

前缀和与差分的定义

前缀和的定义比较简单,对于一个序列 a i , b i a_i,b_i ai​,bi​有下列的定义, b i = a 1 + a 2 + a 3 + ⋅ ⋅ ⋅ + a i , a i = b i − b i − 1 b_i=a_1+a_2+a_3+···+a_i,a_i=b_i-b_{i-1} bi​=a1​+a2​+a3​+⋅⋅⋅+ai​,ai​=bi​−bi−1​,则称 b b b为 a a a的前缀和, a a a为 b b b的差分。显然这两种是互逆操作。

前缀和的应用

前缀和基本上只有一个作用,就是求快速求某个区间的和

一维前缀和

b l = a 1 + a 2 + a 3 + ⋅ ⋅ ⋅ + a l b_l=a_1+a_2+a_3+···+a_l bl​=a1​+a2​+a3​+⋅⋅⋅+al​
b r = a 1 + a 2 + a 3 + ⋅ ⋅ ⋅ + a r b_r=a_1+a_2+a_3+···+a_r br​=a1​+a2​+a3​+⋅⋅⋅+ar​
b r + 1 = a 1 + a 2 + a 3 + ⋅ ⋅ ⋅ + a r + 1 b_{r+1}=a_1+a_2+a_3+···+a_{r+1} br+1​=a1​+a2​+a3​+⋅⋅⋅+ar+1​
我们想求 [ l , r ] [l,r] [l,r]的区间之和,即 a l + a l + 1 + ⋅ ⋅ ⋅ + a r a_l+a_{l+1}+···+a_r al​+al+1​+⋅⋅⋅+ar​,我们可以用 b r + 1 − b l b_{r+1}-b_l br+1​−bl​来求(直接相减就可以得出)。
具体代码如下:

二维前缀和

b x 2 y 2 = ∑ i = 1 x 2 ∑ j = 1 y 2 a i j b_{{x_2}{y_2}}=\sum\limits_{i=1}^{x_2}\sum\limits_{j=1}^{y_2}{{a_{ij}}} bx2​y2​​=i=1∑x2​​j=1∑y2​​aij​

b x 1 x 2 = ∑ i = 1 x 1 ∑ j = 1 y 1 a i j b_{{x_1}{x_2}}=\sum\limits_{i=1}^{x_1}\sum\limits_{j=1}^{y_1}{{a_{ij}}} bx1​x2​​=i=1∑x1​​j=1∑y1​​aij​
b x 2 y 1 − 1 = ∑ i = 1 x 2 ∑ j = 1 y 1 − 1 a i j {b_{{x_2}{{y_1-1{}}}}}=\sum\limits_{i=1}^{x_2}\sum\limits_{j=1}^{y_1-1}{{a_{ij}}} bx2​y1​−1​=i=1∑x2​​j=1∑y1​−1​aij​
b x 1 − 1 y 2 = ∑ i = 1 x 1 − 1 ∑ j = 1 y 2 a i j b_{{{x_1}-1}{y_2}}=\sum\limits_{i=1}^{x_1-1}\sum\limits_{j=1}^{y_2}{{a_{ij}}} bx1​−1y2​​=i=1∑x1​−1​j=1∑y2​​aij​
若想求 ( x 1 , y 1 ) (x_1,y_1) (x1​,y1​)到 ( x 2 , y 2 ) (x_2,y_2) (x2​,y2​)中区域的和,可用 b x 2 y 2 − b x 1 − 1 y 2 − b x 2 y 1 − 1 + b x 1 y 1 b_{{x_2}{y_2}}-b_{{{x_1-1}}{y_2}}-b_{{x_2}{y_1-1}}+b_{{x_1}{y_1}} bx2​y2​​−bx1​−1y2​​−bx2​y1​−1​+bx1​y1​​来求。

具体代码如下:

  1. 求前缀和的代码
for(int i=1;i<=n;i++){ //
    for(int j=1;j<=m;j++){
        b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j];
    }
}
  1. 求区间面积的代码
res=b[x2][y2]-b[x2][y1-1]-b[x1-1][y2]+b[x1-1][y1-1];

差分的应用

差分主要用于对某一区间同时进行加法的操作。

一维差分

有公式 a l = b l − b l − 1 a_l=b_l-b_{l-1} al​=bl​−bl−1​,对 b l b_l bl​加 c c c,则有 a l + c , a l + 1 + c , a l + 2 + c , ⋅ ⋅ ⋅ a_l+c,a_{l+1}+c,a_{l+2}+c,··· al​+c,al+1​+c,al+2​+c,⋅⋅⋅
若想要对 [ L , R ] [L,R] [L,R]中的元素加c,只需要对 b l + c b_l+c bl​+c,然后 b r + 1 − c b_{r+1}-c br+1​−c,然后对 b b b数组求前缀和就可以的出加 c c c操作后的 a a a数组

代码如下:

对差分数组进行插入操作

void insert(int l,int r,int c){
    b[l]+=c;
    b[r+1]-=c;
}

二维差分

二维差分的推理比较难写,大体就是先差分后求前缀和,这里直接给出代码:

在差分数组中进行插入操作

void insert(int x1,int y1,int x2,int y2,int c){
    b[x1][y1]+=c;
    b[x1][y2+1]-=c;
    b[x2+1][y1]-=c;
    b[x2+1][y2+1]+=c;
}

求前缀和

for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]+b[i][j];
        }
    }

小结

这里有一个重点:我们对差分数组进行初始化的时候可以用插入操作直接完成
一维的有insert(l,l,c)
二维的有insert(x,y,x,y,c)

上一篇:2020 蓝桥杯大学 B 组省赛模拟赛 方阵


下一篇:【二维前缀和】AcWing 796.子矩阵的和