前缀:
一维:
原数组:a[i]
前缀和数组:s[i]=a[1]+a[2]+...+a[i];
作用:求区间和[l,r],s[r]-s[l-1];
#include <iostream>
#include <cstdio>
#define in(x) scanf("%d",&x)
using namespace std;
const int maxn=100005;
int a[maxn],s[maxn];
int main(){
int n,m;//n数组长度 m 询问次数
in(n);in(m);
for(int i=1;i<=n;i++){
in(a[i]);
}
for(int i=1;i<=n;i++){
s[i]=s[i-1]+a[i];
}
while(m--){
int l,r;
in(l);in(r);
cout<<s[r]-s[l-1]<<endl;
}
return 0;
}
二维:
差分:
原数组:a[i]
构造出数组b[i],使得a[i]=b[1]+b[2]+...+b[i];
b的构造:b[i]=a[i]-a[i-1];
用处:
- 求b的前缀和(O(n))可得a
- 区间修改:[l,r]内所有数字+c:b[l]+=c,b[r+1]-=c;
#include <bits/stdc++.h>
#define in(x) scanf("%d",&x)
using namespace std;
const int N=100005;
int a[N],d[N];
int n,m;
void change(int l,int r,int c);
int main(){
in(n),in(m);
for(int i=1;i<=n;i++){
in(a[i]);
change(i,i,a[i]);
}
while(m--){
int l,r,c;
in(l);in(r);in(c);
change(l,r,c);
}
int sum=0;
for(int i=1;i<=n;i++){
sum+=d[i];
cout<<sum<<" ";
}
cout<<endl;
return 0;
}
void change(int l,int r,int c){
d[l]+=c;
d[r+1]-=c;
}
二维差分:
我们可以把差分中的构造过程也可以用修改的过程来做(因为可以把原数组看成一个全0数组,然后进行长度为一的区间修改),所以我们不需要去思考查分的构造,而是去直接考虑如何修改。
设原数组a[][]查分数组d[][].
修改过程:
现要修改左上角x1y1右下角x2y2区间加上c(红色),则:
- d[x1][y1]+=c 绿色区域全部加上了c
- d[x2+1][y1]-=c 蓝色区域全部减去c
- d[x1][y2+1]-=c 橙色区域全部减去c
- d[x2+1][y2+1]+=c 紫色区域因为多见了一次 现加回一次。
求值:
求值相当于,前缀和的构造过程。
#include <bits/stdc++.h>
#define in(x) scanf("%d",&x);
using namespace std;
const int N=1005;
int a[N][N],d[N][N];
int n,m;
void fix(int x1,int y1,int x2,int y2,int c){
d[x1][y1]+=c;
d[x2+1][y1]-=c;
d[x1][y2+1]-=c;
d[x2+1][y2+1]+=c;
}
int main(){
int q;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
in(a[i][j]);
fix(i,j,i,j,a[i][j]);
}
}
while(q--){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int c;
in(c);
fix(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1];
printf("%d ",d[i][j]);
}
cout<<endl;
}
return 0;
}