一维前缀和 + 二维前缀和 + 差分の详解(简单易懂)

【引入】

首先给出一个问题:
给定n个数,再给出m个询问,每个询问给出区间Li,Ri和x,要求你在Li到Ri上每一个值都加上x,最后给出一个询问区间L,R的区间和,怎么办?
思考一下:如果暴力,最坏时间复杂度O(n^2);线段树或者树状数组,时间复杂度O(logn);而使用差分可以O(n)。

 要使用差分,首先我们来谈谈前缀和。

【前缀和】

什么是前缀和?前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。 

设b[]为前缀和数组,a[]为原数组,根据这句话可以得到前缀和的定义式和递推式:

  定义式 递推式
一维前缀和  b[i]=\sum_{j=0}^{i}a[j] b[i]=b[i-1]+a[i]
二维前缀和 b[x][y]=\sum_{i=0}^{x}\sum_{j=0}^{y}a[i][j] b[x][y]=b[x-1][y]+b[x][y-1]-b[x-1][y-1]+a[x][y]

一维前缀和理解起来比较容易,二维前缀和后边细说,我们先来说说差分。

 【差分】

什么是差分?差分是一个数组相邻两元素的差,一般为下标靠后的减去靠前的一个。设差分数组p[],即:

p[i]=a[i]-a[i-1]

【联系】

前缀和和差分有什么关系呢?

令F(a)表示前缀和数组,G(a)表示差分数组,则      F(G(a)) = G(F(a)) =a

可以说 前缀和 和 差分 是一对互逆过程。

【一维前缀和】

根据上述表达式我们可以以O(1)求出区间[i,j]的区间和     sum[i,j]=b[j]-b[i-1]

【二维前缀和】

二维前缀和的计算运用了容斥定理,我们来看下图:

一维前缀和 + 二维前缀和 + 差分の详解(简单易懂)_第1张图片

显然左上角重合的那一块加了两次,所以我们需要减掉重合的部分。请结合上图理解下列递推式 (*^▽^*) 。

b[i][j]=b[i-1][j]+b[i][j-1]-b[i-1][j-1]+a[i][j]

【应用】

 回到引入中的问题,对于每一个L,R,x,我们只需要在b[L]+x,在b[R]-x,最后做前缀和,O(1)查询即可。

代码如下:

#include 
int a[1005];
int b[1005];
int main()
{
	int n,m; scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
	while(m--){
        int L,R,x;
        scanf("%d%d%d",&L,&R,&x);
        b[L]+=x;
        b[R+1]-=x;
	}
	int sum=0;
	for(int i=1;i<=n;i++){
        sum+=b[i];
        a[i]+=a[i-1]+sum;
	}
	int l,r; scanf("%d%d",&l,&r);
	printf("%d\n",a[r]-a[l-1]);
	return 0;
}

还是引入中的问题,升级为:

给定一个n*m大小的矩阵a,再给出m个询问,每次询问给定x1,y1,x2,y2和x,表示以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素都加上x,最后给出一个询问:以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素和,怎么办?

对于每一个x1,y1,x2,y2,我们只需要对数组b

b[x1][y1]+=p;  b[x2+1][y2+1]+=p;

b[x2+1][y1]-=p;  b[x1][y2+1]-=p;

然后做二维前缀和即可。 

例题:Monitor(二维前缀和+差分)

你可能感兴趣的:(一维前缀和 + 二维前缀和 + 差分の详解(简单易懂))