差分及二维差分

差分

差分即相邻两个数的差,给定数组 a a a我们能得到其差分数组 d [ i ] = a [ i ] − a [ i − 1 ] , a [ 0 ] = 0 d[i]=a[i]-a[i-1],a[0]=0 d[i]=a[i]a[i1],a[0]=0

差分和前缀和的区别

前缀和求的是 s u m [ i ] = ∑ j = 1 i a [ j ] sum[i] = \sum_{j=1}^i a[j] sum[i]=j=1ia[j],如果使用前缀和表示某个位置的元素,那么 a [ i ] = s u m [ i ] − s u m [ i − 1 ] a[i] = sum[i] - sum[i-1] a[i]=sum[i]sum[i1]。对比差分,我们发现差分和前缀和在预处理和求和的运算刚好是“相逆”的。

区间加法

对于某个位置的差分,影响的是后面所有元素的值(前缀和),而不影响它前面元素的值(前缀和)。

那么我们会发现,如果对一个区间 [ x , y ] [x,y] [x,y]内的所有数都加上一个数 p p p,那么显然只有 d [ x ] d[x] d[x] d [ y + 1 ] d[y+1] d[y+1]的需要改变:

int a[maxn], d[maxn];

void add(int x, int y, int p) {
	d[x] += p;
	d[y + 1] -= p;
}

差分数组求得原数组

不难得知 a [ i ] = ∑ j = 1 i d [ j ] a[i] = \sum_{j=1}^i d[j] a[i]=j=1id[j]

二维差分

前缀和可以衍生到二维前缀和,那么差分也有二维差分,对于如下矩阵,需要类似二维前缀和的求法来求差分矩阵:

1    2    3 4    5    6 7    9    8 1~~2~~3 \\ 4~~5~~6 \\ 7~~9~~8 1  2  34  5  67  9  8

差分矩阵为:

1      1      1 3      0      0 3      1   − 2 1~~~~1~~~~1 \\ 3~~~~0~~~~0 \\ 3~~~~1~-2 1    1    13    0    03    1 2

而对于原矩阵某个位置的值,等价于以这个元素为右下角,原矩阵左上角为左上角的子矩阵中所有元素的和:(原矩阵蓝色方块的值由红色区域加上蓝色方块的差分值求和得到)

在这里插入图片描述
在这里插入图片描述

int a[N][N], d[N][N];

for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= m; j++) {
		cin >> a[i][j];
		d[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
		//前缀和:sum[i][j] = a[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1];
	}
}

子矩阵加法

差分矩阵改变一个数影响的是以该元素为左上角,原矩阵的右下角 ( n , m ) (n,m) (n,m)为右下角形成子矩阵区域。

类似于一维差分,二维差分可以进行快速子矩阵加法,当我们需要对如下的红色区域加上一个数 p p p时,只需要 d [ 1 ] [ 1 ] + p d[1][1]+p d[1][1]+p
在这里插入图片描述
产生的影响为整个矩阵,为了消除其他位置的影响,我们需要将上图白色区域的影响消除,也就是要更新 d [ 3 ] [ 1 ] , d [ 1 ] [ 3 ] , d [ 3 ] [ 3 ] d[3][1],d[1][3],d[3][3] d[3][1],d[1][3],d[3][3],蓝色区域需要减去 p p p即绿色位置减去 p p p,而黄色区域多减了,需要再加上 p p p,即所有紫色区域加上 p p p

在这里插入图片描述

void add(int i, int j, int x, int y, int val) {
    d[i][j] += val, d[x + 1][y + 1] += val;
    d[x + 1][j] -= val, d[i][y + 1] -= val;
}

差分矩阵求得原矩阵

类似于一维差分,当我们需要求得原矩阵时,不可能对于每一个元素和左上角形成的子矩阵求和,这样太麻烦了,实际上我们可以使用二维循环,对于第一列,手算一下不难得出是正确的,然后对于每个元素左上角 2 × 2 2 \times 2 2×2的子矩阵,除了当前元素的位置其余三个位置都是原矩阵的元素,那么我们对上述求差分的过程反着来:

a [ i ] [ j ] = d [ i ] [ j ] + a [ i − 1 ] [ j ] + a [ i ] [ j − 1 ] − a [ i − 1 ] [ j − 1 ] ⇔ a ′ [ i ] [ j ] = d [ i ] [ j ] + d [ i − 1 ] [ j ] + d [ i ] [ j − 1 ] − d [ i − 1 ] [ j − 1 ] a[i][j] = d[i][j] + a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] \Leftrightarrow a'[i][j] = d[i][j] + d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1] a[i][j]=d[i][j]+a[i1][j]+a[i][j1]a[i1][j1]a[i][j]=d[i][j]+d[i1][j]+d[i][j1]d[i1][j1]

这样当我们对差分矩阵动态修改之后就可以 O ( n m ) O(nm) O(nm)得到新的矩阵了

for (int i = 1; i <= n; i++) {
	for (int j = 1; j <= m; j++) {
		d[i][j] = d[i][j] + d[i - 1][j] + d[i][j - 1] - d[i - 1][j - 1];
    }
}
  • 8
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
二维差分是一种常用的数据结构和算法巧,用于高效地处理二维矩阵区间的更新和查询操作。它可以在O(1)的时间复杂度内完成区间的更新和查询操作,相比传统的暴力遍历方法,具有更高的效率。 二维差分的基本思想是将原始矩阵转化为一个差分矩阵,差分矩阵中的每个元素表示原始矩阵中相邻元素之间的差值。通过对差分矩阵进行预处理,可以实现对原始矩阵区间的更新和查询操作。 具体来说,二维差分的操作包括两个步骤:预处理和操作。预处理阶段,需要根据原始矩阵构建差分矩阵;操作阶段,可以通过对差分矩阵的更新来实现对原始矩阵区间的更新,同时可以通过对差分矩阵的求和来实现对原始矩阵区间的查询。 下面是二维差分的基本操作: 1. 构建差分矩阵:对于原始矩阵A,构建一个差分矩阵B,其中B[i][j] = A[i][j] - A[i-1][j] - A[i][j-1] + A[i-1][j-1]。 2. 区间更新:对于原始矩阵A的一个区间[left, right] x [top, bottom],将差分矩阵B的相应位置进行更新,即B[left][top] += val,B[right+1][top] -= val,B[left][bottom+1] -= val,B[right+1][bottom+1] += val。 3. 区间查询:对于原始矩阵A的一个区间[left, right] x [top, bottom],通过求和差分矩阵B的相应位置得到区间和,即sum = B[right][bottom] - B[left-1][bottom] - B[right][top-1] + B[left-1][top-1]。 二维差分可以广泛应用于各种算法问题,例如矩阵区间求和、矩阵区间更新等。它的时间复杂度较低,适用于处理大规模的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值