前缀和与差分
前缀和
即数组前n项和
s
n
=
a
1
+
a
2
+
.
.
.
.
.
.
.
.
+
a
n
−
1
+
a
n
sn=a_1+a_2+........+a_{n -1}+a_n
sn=a1+a2+........+an−1+an
定义
s
0
=
0
s_0=0
s0=0
类似于级数的一种应用
计算公式为s[r]-s[l-1]
#include <iostream>
using namespace std;
const int N=1e5+10;
int q[N],s[N];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&q[i]);
for(int i=1;i<=n;i++)s[i]=s[i-1]+q[i]; //将前项数组和定义到另外一个数组中
while(m--){
int l,r;
scanf("%d%d",&l,&r);
int sum=s[r]-s[l-1];
printf("%d\n",sum);
}
return 0;
}
//或者直接写
for(int =1;i<=n;i++){
cin>>s[i];
s[i]+=s[i-1];
}
矩阵前缀和(二维)
讲一下思路
和小学的算面积题差不多
首先x1y1,x2y2分别是所给矩阵中任意选取的一个小矩阵
分别是其左上角和右上角的坐标
要求是算出这个小矩阵中数的和
- 先求前缀和,二维的前缀和计算的是从0开始的每一个矩阵的元素和
#include <iostream>
using namespace std;
int n,m,q;
const int N=1010;
int a[N][N],s[N][N];
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++)//s[0][0]应当等于0,所以应从下标11开始存数
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++)
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//前缀和求和公式
while(q--){
int x1,x2,y1,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1];根据面积图计算小矩形面积
printf("%d\n",sum);
}
return 0;
}
差分
公
式
:
前
缀
和
:
a
[
n
]
公式:前缀和:a[n]
公式:前缀和:a[n]
差
分
:
b
[
n
]
差分:b[n]
差分:b[n]
b
[
i
]
=
a
[
i
]
−
a
[
i
−
1
]
b[i]=a[i]-a[i-1]
b[i]=a[i]−a[i−1]
类似于求前缀和的逆运算,这次通过前缀和求得差分数组
比较抽象
菜鸡理解:
已知一个前缀和序列a[n],
向前缀和a[n]序列[l,r]中每一个数加上c,
假想一个序列b[n]是构成a[n]的差分数组;
那么要完成这一操作
**只需要在b[l]+c;
并且在b[r+1]-c**
类似的,当l==r时,即向序列a[n]中插入一个数据;
得到前缀和数组a[n];
即递推公式a[i]-a[i-1]=b[i]可视作插入操作的一个特例;
可利用这一性质反过来由前缀和得到差分数组
#include <iostream>
using namespace std;
const int N=100010;
int a[N],b[N];
int n, m;
void insert (int l,int r,int c){
b[l]+=c;
b[r+1]-=c;
}
int main(){
cin>>n>>m;
int l,r,c;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)insert(i,i,a[i]);//通过插入操作获得差分数组b[i],相当于利用了递推公式b[i]=a[i]-a[i-1];
while(m--){
cin>>l>>r>>c;
insert(l,r,c);
}
for(int i=1;i<=n;i++){
b[i]+=b[i-1];
printf("%d",b[i]);
}//利用普通前缀和公式,求出m次操作完之后的最终前缀和数组 ,此时b[i]为最终前缀和数组
//若定义新数组s[N][N]存放最终差分数组,那么这样写也正确s[i]=s[i-1]+b[i];
return 0;
}
差分矩阵
是对应的二阶差分数组,思路一致,注意是二维,写的时候脑子带图
#include <iostream>
using namespace std;
const int N=1010;
int a[N][N],b[N][N];//前缀和二维数组,差分二维数组
int n,m,q;
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(){
cin>>n>>m>>q;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);//通过传值初始化a
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
insert(i,j,i,j,a[i][j]);//通过插入操作间接初始化b
while(q--){
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);//执行q次插入数据操作,得到最终差分数组
}
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];
// s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+b[i][j];或者定义新数组s[N][N]用来存放最后的差分数组,那么这样写也正确
printf("%d ",b[i][j]);
}
printf("\n");
}
return 0;
}