C++ 基础算法 - 二维差分
今天学习二维差分。

一维差分
在学习二维差分之前,先要学会 一维差分
这里加强对一维差分的理解
将一位差分的操作变为前缀的变化,如下列数列 ( 从 0 开始编号 )
将 2 至 5 的数字 ( 对应数列中 7,8,2,1 ) 增加 ,且
,那么差分数组从全为 0 变为
此时差分数组的前缀和将分为三段 ( 相较于原来全为 0 的差分数组 )
第一段为 0 至 1 ( 对应数列中 9,4 ) 的前缀和不变,仍然为 0
第二段为 2 至 5 ( 对应数列中 7,8,2,1 ) 的前缀和增加了 4
第三段为 6 至 7 ( 对应数列中 0,3 ) 的前缀和不变,4 与 -4 相互抵消
正好对应了将 2 至 5 的数字增加 4 的概念
因此我们可以知道,差分数组中每一位的前缀和表示的就是原数列该位置的增值
即 ,
表示的是差分数组
的前缀和,转化后可得
,修指修改后的,原指原来的
所以每一位的增值 ,这就使用数学的方法解释了差分数组的作用,最后我们得到了这样的式子
这样看起来差分的复杂度只有一次修改的复杂度 ,修改
次,复杂度也仅有
次,看起来时间复杂度十分好,但是如果算上查询
次,复杂度变为
,并不高效
因此差分仅适合在数据范围较小的题目使用,遇到数据范围更加大的题目时就需要使用复杂度为 的树状数组和线段树来实现了,但因为差分编码简单,在适合的时候往往选择容易编写的差分

二维差分
由一维差分可以知道,二维差分就是选择一个矩形区域,将区域内的所有元素增加或减少一个数
简单做法 ( 并不正确 )
本段通过思考简单的做法推动二维差分实现,因此本段不是正解,可以选择性阅读本段
修改
先考虑将矩形区域处理一下,变为一条条数列的区间从而使用普通一位差分修改

如上图,将 至
的值加上
,再将
至
修改为
,复杂度为
查询
在查询时,如果询问的矩形区域 ( ) 正好没有与修改矩形区域 (
),的左边线 (
) 相交,这时在统计时就无法加上修改矩形区域修改的值,因此,我们不能从询问的矩形区域的左边线 (
),开始计算,应该从其对应在
轴上的点
与
开始计算
这种方式将每一行都看做一个差分数组,都做了一次差分,时间复杂度为

不难发现,这种方法的时间复杂度比较劣,还有提升的空间
正确做法
本段讲解的才是正确的二维差分
修改
我们尝试将修改操作的复杂度降至 ,我们知道一维差分可以将所有
(
) 的数值增加
,通过两次修改实现区间修改

将这个思想推广至二维,那么二维差分就应该可以将所有 (
) 的数值增加
例如上面三连展示的图片,我们将右下角看做原点, 是要修改的矩形区域,这时候我们将上一段所述的
,那么点 B 左上的所有点都增加了
,但是我们更改的目标是
,如果使用上述方法将会更改整一个
,而如果要消除影响,就可以将
再次减去
看看这个多边形的样子,是不是很熟悉
所以,我们以右上角为原点并加入二维前缀和的思想,并利用公式
上述公式在 二维前缀和 中讲过了
这时候我们需要反过来使用,因为我们先创造了 的影响 ( 增加
),所以我们在后面将消除其影响,就可以使用这个式子,
这三个项就是消除的关键,因为增加了 ,所以
的值也就为
,因此
与
都要等于
以消除影响,但是我们发现
被两次操作都减了一次
,因此需要在
再加上一次
,这其实就是前缀和的思路
由此,我们就用 的时间复杂度记录了差分的影响,代码实现如下 ( 这里我们将左上角看做原点 )
查询
通过修改操作修改了差分的影响,我们发现在图上任意一点,其差分数组的二维前缀和就是其修改的值
因此我们就需要计算差分的前缀和,下面展示的是单次查询
如果全部修改好后进行一次查询可以这么写 ( 其实没什么区别 )
二维差分的代码并不难,较难得地方在于需要一些思考

好了那么这篇文章就讲到这里了,如果你觉得本文对你有所帮助的话,请不要忘记三连支持!
鸽了好久...