差分个人见解(二)
- 二维差分
- 二维差分数组的用途
- 构造二维差分数组
- 实战演练
如果你还不太熟悉一维差分,那么请先学会一维差分。
二维差分
二维差分数组的用途
在一维差分数组中,它的用途是 快速 使一个区间内加上一个数。
那么二维差分数组的用途就是 快速使一个子矩阵内加上一个数。
假设红色的点构成了一个二维数组,现在我们的任务是 将蓝色区域内的元素都加上 c。
首先让绿色的点 加上 c。
此时最终在求前缀和数组的时候,绿色区域内的元素都会加上 c。
接下来关注 紫色点,由于刚才多加了一部分区域,所以我们需要给 紫色点 减去 c。
那么紫色区域内的值都会 减去 c。
接着 黄色点 也减去 c,此时黄色区域内的值在 后面计算前缀和的时候就会 都减去 c。
此时有一部分减多了。
所以这个黑色点再加上 c。
转换成代码如下:(其中 (x1, y1)为子矩阵的左上角的点,(x2, y2) 为子矩阵右下角的点,其中数组b为差分数组)
构造二维差分数组
构造差分数组有两种方法,第一种是利用下列递推公式。
假设下面为我们的差分数组,假设我们要 求下面黑色点的差分。
那么只需要黑色点的前缀和 减去 绿色点的前缀和 减去 蓝色点的前缀和 再加上紫色点的前缀和。 而这些点的前缀和 就是 该坐标 在数组 a (原数组)上对应的值,因为数组a是下面数组的前缀和数组。
第二种就是利用刚才的 addToRange 函数,当子矩阵为一个点的情况。
实战演练
输入一个 n n n 行 m m m 列的整数矩阵,再输入 q q q 个操作,每个操作包含五个整数 x 1 , y 1 , x 2 , y 2 , c x_1, y_1, x_2, y_2, c x1,y1,x2,y2,c,其中 ( x 1 , y 1 ) (x_1, y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2, y_2) (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c c c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数 n , m , q n,m,q n,m,q。
接下来 n n n 行,每行包含 m m m 个整数,表示整数矩阵。
接下来 q q q 行,每行包含 5 5 5 个整数 x 1 , y 1 , x 2 , y 2 , c x_1, y_1, x_2, y_2, c x1,y1,x2,y2,c,表示一个操作。
输出格式
共 n n n 行,每行 m m m 个整数,表示所有操作进行完毕后的最终矩阵。
数据范围
1
≤
n
,
m
≤
1000
1 \le n,m \le 1000
1≤n,m≤1000,
1
≤
q
≤
100000
1 \le q \le 100000
1≤q≤100000,
1
≤
x
1
≤
x
2
≤
n
1 \le x_1 \le x_2 \le n
1≤x1≤x2≤n,
1
≤
y
1
≤
y
2
≤
m
1 \le y_1 \le y_2 \le m
1≤y1≤y2≤m,
−
1000
≤
c
≤
1000
-1000 \le c \le 1000
−1000≤c≤1000,
−
1000
≤
矩阵内元素的值
≤
1000
-1000 \le 矩阵内元素的值 \le 1000
−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2
准备阶段:
输入环节:(注意:前缀和和差分 中 都需要记得 从1开始存储,这样就不需要分类讨论了,会方便很多)
接下来我们需要构造差分数组。
下面为两种构造方法的演示:
当然你得提前 把子矩阵 加上一个数的 函数 先写了。
第一种方法:
第二种方法:
构造完了差分数组,接下来我们来处理 q 次询问。
对于每次询问 输入两个点 和 要加的值。
然后调用函数即可。
最后我们再求 该差分数组 的前缀和数组,然后打印即可。
完整代码如下:
#include <iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void addToRange(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()
{
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++)
addToRange(i, j, i, j, a[i][j]);
while (q--)
{
int x1, y1, x2, y2, c;
scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &c);
addToRange(x1, y1, x2, y2, c);
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
b[i][j] += b[i][j-1] + b[i-1][j] - 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");
}
return 0;
}
完