AcWing算法基础课-前缀和差分

385 阅读2分钟

前缀和差分是互相的逆运算

前缀

注:S数组为前缀和数组,a数组储存的是原数组。

一维前缀和

一维前缀和是求数组的前n个数的和,定义为:

S[n]=a[1]+a[2]++a[n]S[n] = a[1]+a[2]+……+a[n]

在求S[n]S[n]的时候可以用如下公式:

S[n]=S[n1]+a[n]S[n] = S[n-1]+a[n]

一唯前缀和的主要应用是求 a[l]+a[l+1]++a[r]a[l]+a[l+1]+……+a[r] 的值:

a[l]+a[l+1]++a[r]=S[r]S[l1]a[l]+a[l+1]+……+a[r] = S[r]-S[l-1]

主要是用于降低时间复杂度,在每次求a[l]a[l]a[l]a[l]的和的时候,能够将时间复杂度从O(n)O(n)降低到O(1)O(1)

二维前缀和

二维前缀和较一维前缀和较为复杂,需要用到矩阵的相关知识。
二维前缀和主要用于求 S[x1][y1]S[x_1][y_1]S[x2][y2]S[x_2][y_2] 这部分内的所有数的和:

S[x1][y2]S[x21][y1]S[x11][y2]+S[x11][y11]S[x_1][y_2]-S[x_2-1][y_1]-S[x_1-1][y_2]+S[x_1-1][y_1-1]

在求前缀和的时候用到如下公式:

S[i][j]=S[i1][j]+S[i][j1]S[i1][j1]+a[i][j]S[i][j] = S[i-1][j]+S[i][j-1]-S[i-1][j-1] + a[i][j]

同样,二维前缀和的作用也是为了降低时间复杂度。

代码模板

一维前缀和

/*IndexYang 2021-11-03*/ 

#include <iostream>
using namespace std;

const int N = 100010;
int n,m;
int a[N],s[N];
int main(){
	s[0] = 0;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) s[i] = s[i-1]+a[i]; //求前缀和
	while(m--){
		int l,r;
		cin>>l>>r;
		cout<<s[r]-s[l-1]<<endl;
	}
	return 0;
}

二维前缀和

/*IndexYang 2021-11-03*/

#include<iostream>
using namespace std;
const int N = 1010;
int n,m,q;
int a[N][N],s[N][N];

int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>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,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		cout<<s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]<<endl; //求此区域内的数字和 
	}
	return 0;
}

例题

--模板即例题--

差分

一维差分

给定一个原数组a:

a[1],a[2],,a[n]a[1],a[2],……,a[n]

构造一个数组b:

b[1],b[2],,b[n]b[1],b[2],……,b[n]

使得

a[i]=b[1]+b[2]++b[i]a[i] = b[1]+b[2]+……+b[i]

也就是说a数组是b数组的前缀和数组,即b数组则是a数组的差分数组 在构造b数组的时候可以用到如下公式:

b[i]=a[i]a[i1]b[i] = a[i] - a[i-1]

差分最主要的应用是给a数组中的[l,r]区间中的每一个数都加上c,只需对差分数组b做

b[l]+=c,b[r+1]=cb[l] + = c, b[r+1] -= c

再求前缀和就行了。时间复杂度为O(1)O(1), 大大提高了效率。

二维差分

image.png

代码模板

一维差分

 /*IndexYang 2021-11-05*/
 
# include<iostream>
using namespace std;
const int N = 1000010;
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(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) insert(i,i,a[i]);//将原数组的n个数先插入进去,这里是初始化差分数组
	while(m--){
		int l,r,c;
		cin>>l>>r>>c;
		insert(l,r,c); //表示将[l,r]区间上的每个数都加上c
	}
	
	for(int i=1;i<=n;i++){
		b[i] += b[i-1];//将构造的b的差分数组进行求前缀和,恢复成a数组 
		printf("%d ",b[i]); //输出每个都加上c之后的前缀和数组
	}
	return 0;
}

二维差分

/*IndexYang 2021-11-05*/
#include <iostream>
using namespace std;
const int N = 1010;

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[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++){
			cin>>a[i][j];
			insert(i,j,i,j,a[i][j]);
		}
	}
			
	while(q--){
		int x1,y1,x2,y2,c;
		cin>>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];
			printf("%d ",b[i][j]);
		}
		puts("");
	}
	return 0;
}

例题

--模板即例题--