一、定义
对于数组 A = { a 1 , a 2 , . . . , a n } A=\{a_1,a_2,...,a_n\} A={a1,a2,...,an},若存在数组 S S S 使得 S i = a 1 + . . . + a i S_i=a_1+...+a_i Si=a1+...+ai,那么就称数组 S S S 称为数组 A A A 的前缀和。形式基本表现为从原点开始的数据总和。
二、作用
①可以在 O ( 1 ) O(1) O(1) 的复杂度算出A数组中 a l a_l al 到 a r a_r ar 之间所有数的和。
② 区间前缀和可以快速算出一个区间的值的和。
//一维前缀和
for(int i=1;i<=n;i++){
cin>>a[i];s[i]=s[i-1]+a[i];
}
a[l~r]=s[r]-s[l-1];
//二维前缀和
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1];
//三维前缀和
//构造
s[i][j][k]=a[i][j][k]+s[i-1][j][k]+s[i][j-1][k]+s[i][j][k-1]
-s[i-1][j-1][k]-s[i-1][j][k-1]-s[i][j-1][k-1]
+s[i-1][j-1][k-1];
//调用
a[x1y1z1~x1y1z2]=s[x2][y2][z2]
-s[x2][y1][z2]-s[x1][y2][z2]-s[x2][y2][z1]
+s[x1][y1][z2]+s[x1][y2][z1]+s[x2][y1][z1]
-s[x1][y1][z1];
一、795. 前缀和 - AcWing题库
#include
using namespace std;
const int N=1e5+10;
int temp,s[N];
int n,k;
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&temp);
s[i]=s[i-1]+temp;
}
while(k--){
int l,r;scanf("%d%d",&l,&r);
printf("%d\n",s[r]-s[l-1]);
}
return 0;
}
二、796. 子矩阵的和 - AcWing题库
#include
using namespace std;
const int N=1e3+10;
int s[N][N];
int n,m,q;
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int temp;scanf("%d",&temp);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+temp;
}
}
while(q--){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
printf("%d\n",s[x2][y2]+s[x1-1][y1-1]-s[x1-1][ y2]-s[x2][y1-1]);
}
return 0;
}
差分是前缀和的逆运算,若A数组的前缀和是B数组,那么A数组就是B数组的差分。
对于数组 A = { a 1 , a 2 , . . . , a n } A=\{a_1,a_2,...,a_n\} A={a1,a2,...,an},若存在数组 B B B使得 a i = b 1 + . . . + b i a_i=b_1+...+b_i ai=b1+...+bi,那么就称数组B称为数组A的差分。
b 1 = a 1 , b 2 = a 2 − a 1 , b 3 = a 3 − a 2 , . . . , b n = a n − a n − 1 b_1=a_1,b_2=a_2-a_1,b_3=a_3-a_2,...,b_n=a_n-a_{n-1} b1=a1,b2=a2−a1,b3=a3−a2,...,bn=an−an−1。
验证: b 2 + b 1 = a 2 b_2+b_1=a_2 b2+b1=a2。
可以使用 O ( n ) O(n) O(n)的时间从B数组得到A数组。
在A数组的 [ l , r ] [l,r] [l,r]区间内加上c,即 a l + c , a l + 1 + c , . . . , a r + c a_l+c,a_{l+1}+c,...,a_r+c al+c,al+1+c,...,ar+c。
可以将上述操作转化为,在 b l + c b_l+c bl+c等于在 a l , a l + 1 , . . . , a n a_l,a_{l+1},...,a_n al,al+1,...,an上都加上c。但是我们不能改变 a r a_r ar之后的值,于是就将 b r + 1 − c b_{r+1}-c br+1−c就行了。
总结:在A数组的 [ 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。
同样,二维差分叶可以看成是二维前缀和的逆运算。
假设有一个二维矩阵 A i j A_{ij} Aij,其差分矩阵为 B i j B_{ij} Bij,使得 b i j b_{ij} bij到左上角矩形的和等于 a i j a_{ij} aij。
在二维矩阵 A i j A_{ij} Aij中的随机一个矩形,对其中每一个值加上c。
与一维差分相同我们可以将上述操作进行一个转化,对 b i j + c b_{ij}+c bij+c即等于对 a i j a_{ij} aij一直到 a n n a_{nn} ann所围成的矩形都加上c,所以我们再将多余的部分减去就可以了。
总结,在二维矩阵 A i j A_{ij} Aij中的随机一个矩形 ( x 1 , y 1 ) ; ( x 2 , y 2 ) (x_1,y_1);(x_2,y_2) (x1,y1);(x2,y2),对其中每一个值加上c,等同于 b x 1 y 1 + c b_{x_1y_1}+c bx1y1+c, b x 2 + 1 , y 1 − c b_{x_2+1,y_1}-c bx2+1,y1−c, b x 1 y 2 + 1 − c b_{x_1y_2+1}-c bx1y2+1−c, b x 2 + 1 , y 2 + 1 + c b_{x_2+1,y_2+1}+c bx2+1,y2+1+c。
//构造一维差分
void insert(int l,int r,int c){
b[l]+=c;
b[r+1]-=c;
}
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(i,i,a[i]);
}
//调用操作
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
//前缀和
for(int i=1;i<=n;i++)b[i]+=b[i-1];
//构造二维差分
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
insert(i,j,i,j,a[i][j]);
}
//调用操作
while(q--){
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++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
797. 差分 - AcWing题库
关键:在A数组的 [ 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。
#include
using namespace std;
const int N=1e5+10;
int n,m;
int a[N],b[N];
void insert(int l,int r,int c){
b[l]+=c;
b[r+1]-=c;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
insert(i,i,a[i]);
}
while(m--){
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
insert(l,r,c);
}
for(int i=1;i<=n;i++)b[i]+=b[i-1];
for(int i=1;i<=n;i++)printf("%d ",b[i]);
return 0;
}
798. 差分矩阵 - AcWing题库
#include
using namespace std;
const int N=1e3+10;
int n,m,q;
int a[N][N],b[N][N];
void insert(int x1,int y1,int x2,int y2,int c){
b[x1][y1]+=c;
b[x2+1][y1]-=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
insert(i,j,i,j,a[i][j]);
}
while(q--){
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++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)printf("%d ",b[i][j]);
puts("");
}
return 0;
}