差分与前缀和

Posted on 2022-02-16 11:47  ZheyuHarry  阅读(139)  评论(0编辑  收藏  举报

    差分和前缀和其实是一对逆操作,这里将会对前缀和和差分是怎么实施的,如何理解,以及对应一维和二维数组的情况。

 

    前缀和:换言之,就是前n项和,也就是高中学习数列时的Sn;

       Sn = a1+a2+……an;

       Sn+1 = a1+a2+……an+an+1 = Sn+an+1;

    应用:如果要求从l到r的数字之和,我们不需要遍历求和了,只需要调用前缀和序列的Sr和Sl-1,ans = Sr - Sl-1;

               求(x1,y2) 到 (x2,y2)这个子矩阵之和又怎么求呢?  

     二维数组求前缀和

               首先,我们要明白前缀和数组中的这个b[i][j]代表了什么 ;

               

               因为求的是子矩阵之和,我们的b[i][j]表征的是从[1][1]到[i][j]的子矩阵之和;

 

               那么(x1,y1) 到 (x2,y2)的子矩阵之和  ans = b[x2][y2] -b[x1-1][y2] -b[x2][y1-1] +b[x1-1][y1-1] ;

               图示:

               

 

 

                       亮橙色是最终需要的,我们剪掉两个红色部分后,还应加上被重复减去的蓝色部分。

        

                    板子

                    

#include<bits/stdc++.h>
#define maxn 1005

using namespace std;
int a[maxn][maxn],b[maxn][maxn];

int main()
{
int n,m,q;
cin >> n >> m >> q;
//进行数据的读入
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d",&a[i][j]);
//进行数据的处理,求前缀和
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];
}
}
//进行查询
for(int i = 1; i <= q; i++){
int x1,x2,y1,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
printf("%d\n",b[x2][y2]-b[x2][y1-1]-b[x1-1][y2]+b[x1-1][y1-1]);
}
return 0;
}

 

                 差分:因为一维和二维不同,我们可以单纯的将差分理解为前缀和的逆运算。

                 应用:可以多次维护一个区间内数据的修改。

                 一维的情况:b[i] = a[i] -a[i-1];           b[l]+=c;    b[r+1]-=c;

                 二维的情况:对于(x1,y1), (x2,y2) 子矩阵加c,因为这是差分矩阵,我们做如下操作;

                   

                                b[x1][y1]+=c;
                                b[x1][y2+1]-=c;
                                b[x2+1][y1]-=c;
                                b[x2+1][y2+1]+=c;

                  图示:

                

 

 

                                 橙色部分是指+=c的操作对于求前缀和的时候会直接管到矩阵右下角,红色部分是用于-=c抵消掉+=c的操作,而蓝色部分是因为重复了一次-=c 的操作,需要单独+=c一次用于抵消。

               值得注意的是,我们为了得到差分矩阵,我们先令其中的值全部为0,然后再把插入原矩阵中的值,相当于给(i,j)到(i,j)进行加1操作.

       所以我们可以单独写一个insert函数。

               

                   板子:

 

#include<bits/stdc++.h>
#define maxn 1005

using namespace std;
int a[maxn][maxn],b[maxn][maxn],c[maxn][maxn];

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;
}

int main()
{
int n,m,q;
cin >> n >> m >> q;
//进行数据的读入
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d",&a[i][j]);
//先输入原来的值
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <=m; j ++ ){
insert(i,j,i,j,a[i][j]);
}
}
//进行改变值的操作
for(int i = 1; i <= q; i++){
int x1,x2,y1,y2,c;
scanf("%d %d %d %d %d",&x1,&y1,&x2,&y2,&c);
insert(x1,y1,x2,y2,c);
}
//进行数据的处理,求前缀和
for(int i = 1;i <= n; i++){
for (int j = 1; j <= m; j ++ ){
c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+b[i][j];
}
}
//输出ans
for(int i = 1;i <= n; i++){
for (int j = 1; j <= m; j ++ ){
printf("%d ",c[i][j]);
}
printf("\n");
}
return 0;
}