差分解题详解(通俗易懂,包括二维差分)

本文详细介绍了差分数组的概念及其在处理区间加法操作时的高效性,通过构造b数组并进行插入操作,将原本O(n)的时间复杂度降低到O(1)。此外,还探讨了二维差分在处理矩阵加法问题上的应用,通过类似的方法实现对子矩阵的快速更新。文章以实例和AC代码的形式帮助读者理解差分技术在实际问题中的运用。
摘要由CSDN通过智能技术生成

差分详解(里面包括二维差分)

差分的定义

给你一组数据 a 1 a 2 a 3 … … a n a1 a2 a3 …… an a1a2a3an​​,你需要构造一个数组使得 b 1 b 2 b 3 … … b n b1 b2 b3 …… bn b1b2b3bn​,使得 b b b​数组的前缀和是 a a a​.(注意:b数组是自己需要构造的)

b 1 = a 1 b1=a1 b1=a1

b 2 = a 2 − a 1 b2=a2-a1 b2=a2a1

… …

b n = a n − a n − 1 bn=an-an-1 bn=anan1

a数组就称为b的前缀和,b数组就称为a的差分。

何时可以用到差分

我们有了b数组,如何来求出原数组呢?

就是对b数组求一遍前缀和。

给你一组数字 a 1 a 2 a 3 … … a n a1 a2 a3 …… an a1a2a3an​​​,现在要对这一组数字里面的一些数字中从 a l al al a r ar ar中的每一个数都加上 c c c,然后要你求修改后区间 [ l , r ] [l,r] [l,r]​​以内的和。

1:构造b数组

2:将 b r br br​加上 c c c​​ ,联想a数组的含义, a数组就是b数组的前缀和,所以 a r + c ar+c ar+c, a r + 1 + c ar+1+c ar+1+c,……, a n + c an+c an+c

3:但是我们需要保证 [ r + 1 , n ] [r+1,n] [r+1,n]里面的数字不能加上c,所以将 b r + 1 − c br+1-c br+1c​, 就可以保证 a r + 1 − c ar+1-c ar+1c, a r + 2 − c ar+2-c ar+2c,…… a n − c an-c anc.

所以我们插入数组的主要步骤就是
{ b l + c b r + 1 − c \begin{cases} bl+c\\ br+1-c \end{cases} {bl+cbr+1c
本来修改 [ l , r ] [l,r] [l,r]​里面的数需要 o ( n ) o(n) o(n)的时间复杂度,现在只需要修改两个数字的值,复杂度变成了 o ( 1 ) o(1) o(1)

如何快速构造出b数组来

1:我们想假象一下a数组里面的数全部是0,由定义差分数组b也全部是0;

0 0 0 0 0 0

a 1 a1 a1 a 2 a2 a2 a 3 a3 a3 a 4 a4 a4 …… a n − 1 an-1 an1 a n an an

2:但是这只是我们的假象,实际的a数组不是0,所以我们需要进行n次插入操作。

3:下面进行n次插入操作

[ 1 , 1 ] + a 1 [1,1]+a1 [1,1]+a1

[ 2 , 2 ] + a 2 [2,2]+a2 [2,2]+a2

[ 3 , 3 ] + a 3 [3,3]+a3 [3,3]+a3

………………

[ a n , a n ] + a n [an,an]+an [an,an]+an

4:经过n次插入操作之后就将b数组构造出来了(根据前面插入操作的定义,插入操作是将 [ l , r ] [l,r] [l,r]区间内的每一个数都加上 c c c​)

综上所述:差分总共就一个操作,就是插入操作

例子

题意:现在给你一个长度为n整数序列,现在有m个操作,我们需要将 [ l , r ] [l,r] [l,r]中的每一个数都加上 c c c​,请你输出进行完操作后的所有序列。

输入:第一行为n和m;

​ 第二行为n个整数,表示整数序列;

​ 接下来m行每一行有三个数,分别是l,r,c.

​ (其中1<=n,m<=100000 1<=l<r<=n -1000<=c<=1000 -1000<=序列中元素的值<=1000)

ac代码

#include<iostream>
using namespace std;

const int N = 100010;
int a[N], b[N];

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

int main() {
	int n, m;
	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]);

	while (m--) {
		int l, r, c;
		scanf("%d%d%d", &l, &r, &c);
		insert(l, r, c);
	}
	for (int i = 1; i <= n; i++) {
		b[i] += b[i - 1];
		printf("%d ", b[i]);
	}
	return 0;
}

二维差分详解

有了一维差分的基础后,我们对于二维差分就很轻松了,一维差分处理的是一个区间,二维差分处理的是一个子矩阵,我们每次对子矩阵里面的每一个数都加上c,最后要我们求出修改后的矩阵里面的每一个数。

1:构造 b [ i ] [ j ] b[i][j] b[i][j]数组

b [ i ] [ j ] b[i][j] b[i][j]数组的定义: a [ i ] [ j ] a[i][j] a[i][j]数组是 b [ i ] [ j ] b[i][j] b[i][j]数组的前缀和,这个数组是我们假象出来的然后满足某种性质。

我们在1*1的子矩阵内插入一个数 a [ i ] [ j ] a[i][j] a[i][j]

2:插入操作的求法

在这里插入图片描述
其中 b [ x + 1 ] [ y + 1 ] b[x+1][y+1] b[x+1][y+1]就是绿色部分的面积和红色部分的面积。

题目:
在这里插入图片描述

ac代码

#include<iostream>
using namespace std;

const int N = 1010;
int a[N][N], b[N][N];

void insert(int x1, int y1, int x2, int y2,int c) {
	b[x1][y1] += c;
	b[x2+1][y1] -= c;
	b[x1][y2 + 1] -= c;
	b[x2 + 1][y2 + 1] += c;
}

int main() {
	int n, m,q;
	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_s("%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]);
		printf("\n");
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

米兰的小码匠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值