差分

791 字
4 分钟
差分

差分#

差分可以看作前缀和的反向操作。前缀和擅长快速查询区间和,差分擅长快速做区间修改。

如果题目经常出现:

把区间 [l, r] 中每个数都加上 c

那么差分就是很适合的工具。

1、一维差分的思想#

假设原数组是 a,差分数组是 b:

b[1] = a[1]
b[2] = a[2] - a[1]
b[3] = a[3] - a[2]
...

那么对 b 求前缀和,就能还原 a:

a[i] = b[1] + b[2] + ... + b[i]

2、为什么区间加能变成 O(1)#

如果想让 [l, r] 都加上 c,只需要:

b[l] += c
b[r + 1] -= c

原因是:

  • 从 l 开始,前缀和都会多加 c。
  • 到 r + 1 时,再减掉 c,让后面的数恢复原样。

3、一维差分模板#

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N];
void insert(int l, int r, int c) {
b[l] += c;
b[r + 1] -= c;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
insert(i, i, a[i]);
}
while (m--) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}
for (int i = 1; i <= n; i++) {
b[i] += b[i - 1];
printf("%d ", b[i]);
}
return 0;
}

4、二维差分解决什么问题#

二维差分对应的是矩阵里的子矩形修改:

把左上角 (x1, y1) 到右下角 (x2, y2) 的所有元素都加上 c

如果直接遍历子矩形,复杂度可能很高。二维差分可以把一次矩形修改变成 O(1)。

5、二维差分公式#

对子矩形加 c:

b[x1][y1] += c
b[x2 + 1][y1] -= c
b[x1][y2 + 1] -= c
b[x2 + 1][y2 + 1] += c

最后对差分矩阵做二维前缀和,就能得到修改后的原矩阵。

6、二维差分模板#

#include <bits/stdc++.h>
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void insert(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]);
insert(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);
insert(x1, y1, x2, y2, c);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1];
printf("%d ", b[i][j]);
}
printf("\n");
}
return 0;
}

7、差分和前缀和的关系#

可以这样理解:

差分数组 --做前缀和--> 原数组
原数组 --相邻项相减--> 差分数组

所以差分最后一定要“还原”,也就是做一次前缀和。

8、容易写错的地方#

(1)一维区间右端要写 r + 1#

b[r + 1] -= c;

这是让影响在 r 后面停止。

(2)二维右下角是 x2 + 1, y2 + 1#

b[x2 + 1][y2 + 1] += c;

不要写成 b[x2][y2] += c。

(3)还原二维矩阵时列数用 m#

for (int j = 1; j <= m; j++)

不要把列循环写成 j <= n。

总结#

差分适合“多次区间修改,最后一次性输出结果”的问题。

一维:b[l] += c, b[r + 1] -= c
二维:四个角做加减

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

差分
https://blog.tjcheng.cn/posts/migrated/old-78/
作者
TengJinCheng
发布于
2025-10-03
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录