二维差分
深入讲解二维差分矩阵的原理,用墨水扩散模型直观解释四点操作,掌握矩形区域修改的核心思想。
这是个非常好的问题!二维差分是解决“区域修改”问题的神器,理解它为什么是这四个点进行 +1, -1 操作是掌握它的关键。
我们将从一维差分开始,逐步推导到二维,并配合图示来直观理解。
一、 预备知识:前缀和与差分的关系
- 一维前缀和:定义数组
的前缀和数组 为 。 - 性质:区间
的和 = 。
- 性质:区间
- 一维差分:定义数组
的差分数组 为 (其中 )。 - 性质:数组
是数组 的前缀和,即 。
- 性质:数组
一维差分的妙用:区间修改
假设我们要给数组
- 朴素做法:遍历
到 ,执行 。时间复杂度 ,最坏 。 - 差分做法:我们只需要操作两个点!
D[L] += vD[R+1] -= v
为什么有效?
当我们对修改后的差分数组
- 对于
的位置:前缀和 没有受到任何影响,数值不变。 - 对于
的位置:前缀和累加过程包含了 D[L] += v这一项,但还没遇到D[R+1] -= v。所以这些位置的比原来的 多了 。 - 对于
的位置:前缀和累加过程既包含了 D[L] += v,也包含了D[R+1] -= v。一加一减抵消了,所以这些位置的数值也不变。
总结:一维差分用两个点的操作,实现了对一段连续区间的修改。
二、 推广到二维:从线到面
二维差分的思想是完全一样的:我们构建一个差分矩阵
如果我们想让矩阵
我们利用二维前缀和的定义:
也就是说,
现在,我们来模拟一下操作:
1. 操作一:D[x1][y1] += v
当我们执行这个操作后,对差分矩阵求前缀和来还原矩阵
对于任何坐标
效果:以
[Image showing D[x1][y1] += v affects the bottom-right infinite region]
如图所示,绿色区域内的所有点的值都增加了
2. 操作二:D[x2+1][y1] -= v
我们需要把目标区域下方多加的部分剔除。我们选择在
当还原矩阵时,对于任何
效果:抵消了目标区域下方的增长。现在受影响增加
[Image showing D[x2+1][y1] -= v cancels out the region below the target]
如图所示,操作后,深绿色区域是目前净增加
3. 操作三:D[x1][y2+1] -= v
同样的道理,我们需要把目标区域右方多加的部分剔除。我们选择在
效果:抵消了目标区域右方的增长。
[Image showing D[x1][y2+1] -= v cancels out the region to the right of the target]
如图所示,操作后,深绿色区域是目前净增加
4. 操作四:D[x2+1][y2+1] += v(关键的补回)
仔细观察上面的两步抵消操作:
- 操作二减去了目标下方的区域(包含右下角那个重叠区域)。
- 操作三减去了目标右方的区域(也包含右下角那个重叠区域)。
这就导致目标区域右下方的那个区域(即
所以,这个区域现在的值变为:原值
所以,我们在两者交界的起始点
最终效果:
- 目标区域内(
):只受 D[x1][y1] += v的影响。净增量。 - 目标下方:受
D[x1][y1]() 和 D[x2+1][y1]() 影响。净增量 。 - 目标右方:受
D[x1][y1]() 和 D[x1][y2+1]() 影响。净增量 。 - 目标右下方:受全部四个操作影响 (
)。净增量 。
[Image showing final state after D[x2+1][y2+1] += v, only target region is affected]
完美!通过这四步操作,我们精确地只让目标矩形区域内的数值增加了
数学公式总结
我们利用了二维前缀和的容斥原理的逆过程。
要让原矩阵
考察还原后的矩阵
为了只让
- 在
处 :启动增量波,覆盖右下方所有区域。 - 在
处 :在目标下方截止增量波。 - 在
处 :在目标右方截止增量波。 - 在
处 :修复因两次截止而导致右下方区域被多扣除的错误。
这就是二维差分四个操作点的数学原理,本质上是巧妙运用了前缀和的累加特性和容斥原理来控制影响范围。
非常抱歉!之前的回答中承诺的图示没有正常显示,这确实让抽象的数学原理变得难以理解。
我们将这个问题重新来过。这次我将使用文本字符画来模拟图示,希望能帮你直观地get到二维差分的精髓。
核心原理:墨水扩散模型
为了理解二维差分,我们需要建立一个核心的直觉模型:“墨水扩散”。
我们有两个矩阵:
矩阵(原图):我们最终想要修改的地图即题目中的 grid。矩阵(差分图):我们的操作台。
它们的关系(关键!):
矩阵
这意味着:如果我们改变差分图
图示理解“扩散”:
假设我们在
Plaintext
D图操作:D[2][2] += v
还原后的A图受影响区域:
. . . . .
. # # # # <- 第2行及下方
. # # # #
. # # # #
. # # # #
^
第2列及右侧
只要坐标
目标:精准打击一个矩形区域
我们的任务:只让红色框选的矩形区域(从
我们将使用“包含-排除”法(容斥原理),分四步来达成这个目标。
步骤 1:首次覆盖(范围太大了!)
操作:D[x1][y1] += v
含义:在目标矩形的左上角滴入墨水。
效果图:墨水向右下方扩散,覆盖了目标区域,但也覆盖了目标下方和右方的所有区域。
Plaintext
(假设 x1=2, y1=2, x2=3, y2=3)
目标是中间的 2x2 区域。
操作:D[2][2] += v
A图当前状态(+ 表示增加了 v):
. . . . .
. + + + +
. + + + +
. + + + +
. + + + +
当前状态:目标区域有了
步骤 2:切掉下方多余部分
操作:D[x2+1][y1] -= v
含义:在目标矩形底边的下一行,滴入“去色剂”(
效果图:去色剂也向右下方扩散,抵消了步骤1中扩散到下方的墨水。
Plaintext
操作:D[4][2] -= v (因为 x2=3, 所以 x2+1=4)
A图当前状态:
. . . . .
. + + + + <- 这一行受 D[2][2] 影响 (+v)
. + + + + <- 这一行受 D[2][2] 影响 (+v)
. . . . . <- 这一行受 D[2][2](+v) 和 D[4][2](-v) 共同影响,抵消为0
. . . . . <- 同上
当前状态:目标区域和它右边的区域有
步骤 3:切掉右侧多余部分
操作:D[x1][y2+1] -= v
含义:在目标矩形右边的下一列,滴入“去色剂”(
效果图:去色剂向右下方扩散,抵消了步骤1中扩散到右侧的墨水。
Plaintext
操作:D[2][4] -= v (因为 y2=3, 所以 y2+1=4)
A图当前状态:
. . . . .
. + + . . <- 右侧受 D[2][2](+v) 和 D[2][4](-v) 影响,抵消为0
. + + . . <- 同上
. . . ? ? <- 注意右下角这个区域!
. . . ? ?
当前状态:目标区域完美了!目标正下方和正右方也被切掉了。但是右下角出了问题。
步骤 4:修复右下角的误伤(关键!)
让我们看看右下角(即 ? 的位置)发生了什么。
它经历了三次波次:
- 步骤 1 的墨水扩散:带来
。 - 步骤 2 的去色剂扩散:带来
。 - 步骤 3 的去色剂扩散:带来
。
净结果:
右下角区域的值不仅没保持不变,反而变小了!这是因为我们切下方和切右方时,右下角这块公共区域被切了两次。
操作:D[x2+1][y2+1] += v
含义:在右下角的交界处,补加一滴墨水,把多扣的补回来。
Plaintext
操作:D[4][4] += v
A图最终状态:
. . . . .
. + + . .
. + + . .
. . . . . <- 右下角受 +v, -v, -v, +v 四个操作影响,最终抵消为0
. . . . .
总结
这四个点的操作本质上就是一次完美的“填补与切割”游戏:
D[x1][y1]++:先铺满一大片,保证目标区域被覆盖。D[x2+1][y1]--:把目标下面的多余区域切掉。D[x1][y2+1]--:把目标右面的多余区域切掉。D[x2+1][y2+1]++:因为第2步和第3步都切掉了右下角,导致右下角被多切了一次,所以要补回来。