差分

差分

差分与前缀和互为逆运算。对于一个普通数组a[ ],通过构造差分数组b[ ]可以使得a[i] = b[1]+b[2]+....+b[i]。
我们发现a[ ]数组是b[ ]数组的前缀和,而b[ ]数组就是a[ ]数组的差分。通过b[ ]数组求a[ ]数组就是求前缀和的过程,其时间复杂度为O(n)。

如何构造差分数组呢?

朴素公式:b[n] = a[n] - a[n - 1]

insert函数:也就是下面作用里讲到的,只不过在这里我们假设数组a[]全部为0,那么b[]也全为0。在此基础上调用insert(i,i,a[i]),实际就是在b[i] + a[i]、b[i + 1] - a[i]去构造数组。就相当于在数组a[ ]的区间[i,i]加a[i],说明这种做法是正确的。

PS:这里有点绕不能理解就用朴素公式,但是注意这里你理解了,接下来的二维差分就能用insert函数构造差分数组。

void insert(int l,int r,int c)
{
    b[l] += c;
    b[r + 1] -=c;
}

作用:如果现在我们要对数组a[ ]的[l,r]项都加上c,如果我们暴力扫一遍时间复杂度为O(n),但是通过差分可以做到O(1)时间复杂度。具体操作是在b[l] + c,那么a[l]...a[n]都会加上c。接着在b[r + 1] - c,那么a[r + 1]...a[n]就会不受加c的影响。

797 差分

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100010;

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(){
	scanf("%d%d",&n,&m);
	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] = a[i] - a[i - 1]; 
	
	while(m--)
	{
		int l, r, c;
		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];//构造前缀和 
	
	for (int i = 1; i <= n; i ++ ) printf("%d ",b[i]);
    return 0;
}

/*
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

3 4 5 3 4 2
*/

798 差分矩阵

二维差分

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 1010;
int n,m,q;
int a[N][N],b[N][N];

//核心 
void insert(int i1,int j1,int i2,int j2,int c)
{
	b[i1][j1] += c;
	b[i1][j2 + 1] -= c;
	b[i2 + 1][j1] -= c;
	b[i2 + 1][j2 + 1] += c;
}

int main(){
	scanf("%d%d%d",&n,&m,&q);
	for (int i = 1; i <= n; i ++ )
		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 ++ )
			insert(i,j,i,j,a[i][j]);
	
	while (q--)
	{
		int x1,y1,x2,y2,c;
		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 - 1][j] + b[i][j - 1] - b[i-1][j-1];//计算前缀和矩阵 
		
	for (int i = 1; i <= n; i ++ )
	{
		for (int j = 1; j <= m; j ++ )
			printf("%d ",b[i][j]);
			puts("");	
	}

    return 0;
}

/*
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1

2 3 4 1
4 3 4 1
2 2 2 2
*/

习题

posted @ 2021-04-17 21:29  Treasure_lee  阅读(205)  评论(0编辑  收藏  举报