😎前缀和就是:
给定序列a[n],它的前缀和序列s[n]的s[i]=a[1]+..a[i]
前缀和序列的用处:解决多次询问区间[ i , j ]内a序列的和
区间求和时间复杂度o(1)
暴力区间求和时间复杂度o(N)
acwing795 一维前缀和
#include <bits/stdc++.h>//一维前缀和 using namespace std; const int N=100005; int n,m,l,r; int a[N],s[N]; int main() { cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&a[i]),s[i]=s[i-1]+a[i];//按定义构造前缀和序列s while(m--){ scanf("%d%d",&l,&r); cout<<s[r]-s[l-1]<<endl;//r及之前的减去l之前的为l~r } return 0; }acwing796 二维前缀和
s[ i , j ]表示矩阵0,0至i,j的数和
#include <bits/stdc++.h>//二维前缀和 #define y1 yy //acwing全局不可用y1 using namespace std; int n,m,q,x1,y1,x2,y2,c; const int N=1004; int a[N][N]={0},s[N][N]={0}; int main(int argc, char** argv) { cin>>n>>m>>q; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) scanf("%d",&s[i][j]), s[i][j]+=s[i][j-1]+s[i-1][j]-s[i-1][j-1];//定义构造s矩阵,此处涉及容斥定理(核心) while(q--){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2,&c); printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);//是y1-1,x1-1,而不是x1,y1,是这个点的上一个数 (核心) }//涉及容斥定理 return 0; }
😎差分就是:给定序列a[n],差分序列(唯一的)b[n]满足a[i]=b[1]+..b[i],即前缀和的逆
差分序列的用处:解决多次在区间[ i , j ] 内所以数全部加上或减去一个数c,极少次(基本就最后一次求数组状态)求数组状态
差分区间连加时间复杂度o(2),求数组状态复杂度o(N);
和线段树及树状数组的区别:1.差分是多次对区间内所有数加减c,而树状数组是多次对某一个数加减c
2.差分是极少次求数组状态,而树状数组的频繁求数组状态
差分序列b 的构造可以看出原本a序列全部为0,每次读入a[ i ]就在区间[ i , i ]内插入a[ i ]
求原数组就按求前缀和序列的方法求
acwing797一维差分
#include <bits/stdc++.h>//一维差分 using namespace std; const int N=100004; int n,m,a[N],b[N],l,r,c; void insert(int l,int r,int c){//在区间[l,r]内所有数+c(核心) b[l]+=c; b[r+1]-=c; } int main(int argc, char** argv) { cin>>n>>m; for(int i=1;i<=n;i++) scanf("%d",&a[i]),insert(i,i,a[i]);//在区间【i,i】内所有数+a[i]的方式构造差分序列 while(m--){ scanf("%d%d%d",&l,&r,&c); insert(l,r,c);//在区间[l,r]内所有数+c } for(int i=1;i<=n;i++) b[i]+=b[i-1],printf("%d ",b[i]);//用求前缀和的方法求出原数组(差分的前缀和)(核心) printf("\n"); return 0; }
acwing798二维差分
对于a[ i , j ],矩阵b满足0,0到 i , j 的和等于a[ i,j ]
#include <bits/stdc++.h> #define y1 yy using namespace std; int n,m,q,x1,y1,x2,y2,c; const int N=1005; int a[N][N],b[N][N]; void insert(int x1,int y1,int x2,int y2,int 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; } int main(int argc, char** argv) { cin>>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]);//在区间i,j到i,j内全部加a[i,] while(q--){ 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][j-1]+b[i-1][j]-b[i-1][j-1], printf("%d ",b[i][j]);//按求前缀和的方法求原数组(核心) printf("\n"); } return 0; }