一维、二维差分

本文为学习完两篇有关差分数组的博客后,记录下的学习总结,如果看完本文不太理解,可以移步其他两篇博客:

一维差分讲解

二维差分讲解

一维差分

假设原数组表示为 a[0~n],差分数组为 d[0~n+1]。初始化差分数组:

① d[0] = a[0] = 0

② d[i] = a[i] - a[i - 1], for i in [1, n]

③ d[n + 1] = 0 - a[n]

可见,一维差分数组记录的是两个相邻数之间的差,即 d[i] 表示 a[i] 与 a[i - 1] 之差。

需要注意的一点是,差分数组的大小一般会开为 n + 2,并令 d[0] = 0,这一步主要是为了方便进行操作②时,不用特判 i = 1 的情况。另一方面,令 d[n + 1] = 0 - a[n],是为了方便后续对差分数组进行修改时,不用进行特判(后面看一下差分数组的修改操作应该就会理解了),这里也可以理解为 a[n + 1] 为 0,d[n + 1] 就表示 a[n + 1] 与 a[n] 之差。

一维差分数组具有如下性质:

① 对 a 数组在 [l, r] 区间上的修改, 可以转变为对 d 数组在 l, r + 1 位置上的修改, 如对 a[l~r] 区间加上 c, 只需对 d[l] += c, d[r + 1] -= c 即可, 这是差分数组最重要的性质之一, 即将区间修改转为单点修改。

② SUM(d[0]~d[i]) = a[i],前缀和性质。

一维差分数组的常见应用场景为,给定一个长为 n 数组,有 m 次操作,每次操作给数组指定区间内的所有元素加上或减去一个值,要求输出 m 次修改后的数组。这种问题我们就可以用一维差分数组来解决,将 m 次区间操作改为 m 次单点操作,最后再根据前缀和性质,将最终修改后的数组还原回来。如果题目要求修改,改为每次修改后,要求输出给定位置的那个元素的值,也可以用树状数组来维护差分数组,这样就可以在 O(logn) 的时间内求出指定元素的值。

模板题:一维差分模板题

Python代码:

"""
一维差分
设原数组为 a[0~n], 差分数组为 d[0~n+1], d[0] = a[0] = d[n + 1] = 0
d[i] = a[i] - a[i - 1]
可见一维差分数组记录的是相邻两个数之间的差
一维差分数组具有如下性质:
1. d[0~i] = a[i], 前缀和性质
2. 对 a 数组在 [l, r] 区间上的修改, 可以转变为对 d 数组在 l, r + 1 位置上的修改, 如对 a[l~r] 区间加上 c,
   只需对 d[l] += c, d[r + 1] -= c 即可, 这是差分数组最重要的性质之一, 即区间修改转为单点修改
模板题: http://47.110.135.197/problem.php?id=5226
"""
import sys


n, m = [int(string) for string in sys.stdin.readline().strip().split()]
d, a = [0 for _ in range(n + 2)], [0 for _ in range(n + 1)]
data = [int(string) for string in sys.stdin.readline().strip().split()]
for i in range(1, n + 1):
    a[i] = data[i - 1]
    d[i] = a[i] - a[i - 1]
d[n + 1] = -a[n]

while m:
    m -= 1
    l, r, c = [int(string) for string in sys.stdin.readline().strip().split()]
    d[l] += c
    d[r + 1] -= c

for i in range(1, n + 1):
    d[i] += d[i - 1]
    if i != n:
        print(d[i], end=' ')
    else:
        print(d[i])

神奇的是上述代码只能通过94%的样例,不清楚哪里出错了(感觉没错呀。。。用C写就过了),如果有人发现错误,欢迎评论区纠正。

二维差分

一维差分能将区间修改改为单点修改, 二维差分能将矩阵修改改为单点修改。然而,一维差分数组的含义很明显,相比较之下,二维差分数组的含义就不像一维那么明显了。

假设原二维数组为 a[0~n][0~m],二维差分数组为 d[0~n+1][0~m+1]。初始化差分数组:

① d[0][0~m+1] = a[0][0~m] = 0,d[0~n+1][0] = a[0~n][0] = 0

② d[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1], for i in [1, n], for j in [1, m]

③ d[n + 1][j] = -a[n][j] + a[n][j - 1], for j in [1, m], d[i][m + 1] = -a[i][m] + a[i - 1][m], for i in [1, n]

④ d[n + 1][m + 1] = a[n][m]

其实③和④都是根据②推出来的,只要记住①和②,就能推出所有初始化式子了。式子②可以借助二维数组前缀和的更新方式进行记忆。

二维差分数组具有如下性质:

① 对 a 数组在 [l1, r1], [l2, r2] 区间上的修改, 可以转变为对 d 数组在 (l1, r1), (l2 + 1, r1), (l1, r2 + 1), (l2 + 1, r2 + 1)位置上的修改, 如对 a[l1~r1][l2~r2] 数组加上c, 可转变为: d[l1][r1] += c, d[l1][r2 + 1] -= c, d[l2 + 1][r1] -= c, d[l2 + 1][r2 + 1] += c

② 二维差分数组还原修改后数组的方式与一维数组不太相同,但仍然不难记忆:

for i in range(1, n + 1):
      for j in range(1, m + 1):
          d[i][j] += d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1]
"""
最后d[1~n][1~m]即为最终修改后的二维数组
"""

二维差分数组的常见应用场景为,给定一个行为 n,列为 m 的矩阵,有 p 次操作,每次操作给指定子矩阵内的所有元素加上或减去一个值,要求输出 p 次修改后的矩阵。这种问题我们就可以用二维差分数组来解决,将 p 次区间操作改为 p 次单点操作,最后再根据性质②,将最终修改后的矩阵还原回来。如果题目要求修改,改为每次修改后,要求输出给定位置的那个元素的值,本人猜测可以参照用树状数组维护一维差分数组一样,也可以用二维树状数组来维护二维差分数组,但本人对二维树状数组不熟,有兴趣者可以自己尝试。

模板题:二维差分模板题

Python代码

"""
二维差分
一维差分能将区间修改改为单点修改, 二维差分能将矩阵修改改为单点修改
二维差分的定义的含义就不像一维那么明显了
设 a[0~n][0~m] 为原矩阵, d[0~n+1][0~m+1] 为二维差分矩阵
d[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1] (与二维前缀和初始化方式很像)
二维差分数组具有如下性质:
1. 对 a 矩阵在 [l1, r1], [l2, r2] 区间上的修改, 可以转变为对 d 矩阵在 (l1, r1), (l2 + 1, r1),
  (l1, r2 + 1), (l2 + 1, r2 + 1)位置上的修改, 如对 a[l1~r1][l2~r2] 矩阵加上p, 可转变为:
  d[l1][r1] += p, d[l1][r2 + 1] -= p, d[l2 + 1][r1] -= p, d[l2 + 1][r2 + 1] += p
2. 还原修改后矩阵的方法:
  for i in range(1, n + 1):
      for j in range(1, m + 1):
          d[i][j] += d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1]
模板题: http://47.110.135.197/problem.php?id=5227
"""
import sys


n, m, q = [int(string) for string in sys.stdin.readline().strip().split()]
d, a = [[0 for _ in range(m + 2)] for __ in range(n + 2)], \
       [[0 for _ in range(m + 1)] for __ in range(n + 1)]
for i in range(1, n + 1):
    data = [int(string) for string in sys.stdin.readline().strip().split()]
    for j in range(1, m + 1):
        a[i][j] = data[j - 1]
        d[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1]
for i in range(1, n + 1):
    d[i][m + 1] = -a[i][m] + a[i - 1][m]
for j in range(1, m + 1):
    d[n + 1][j] = -a[n][j] + a[n][j - 1]
d[n + 1][m + 1] = a[n][m]

while q:
    q -= 1
    x1, y1, x2, y2, c = [int(string) for string in sys.stdin.readline().strip().split()]
    d[x1][y1] += c
    d[x2 + 1][y1] -= c
    d[x1][y2 + 1] -= c
    d[x2 + 1][y2 + 1] += c

for i in range(1, n + 1):
    for j in range(1, m + 1):
        d[i][j] = d[i][j] + d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1]
        if j != m:
            print(d[i][j], end=' ')
        else:
            print(d[i][j])

上述代码通过100%样例。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值