文章目录
- 前缀和
-
- 引入
- 一维前缀和
-
- 思路
- 代码模板
- 二维前缀和
-
- 代码模板
- 差分
-
- 引入
- 一维差分
-
- 代码模板
- 二维差分
-
- 代码模板
前缀和
引入
- 解决的问题:求序列/矩阵一段区间内的和。
- 解决问题的方法:使用一个数组专门存放每个数及之前所有数的和,通过该数组对应区间的差值求和。
一维前缀和
思路
前缀和的思路还是比较简单的,序列中每个数都有一个对应数字,记录截止该数为止所有数的和,那么前5个数的和减去前3个数的和,自然就是第4个数到第5个数的和。

由此可以得出要求出
[
l
,
r
]
[l, r]
[l,r] 区间的数的总和,设对应数组为sum,其公式为:
s
u
m
[
r
]
?
s
u
m
[
l
?
1
]
sum[r]-sum[l - 1]
sum[r]?sum[l?1]
代码模板
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5+5;
int a[N], b[N], l, r;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
b[i] += a[i] + b[i - 1];
}
while(m --) {
scanf("%d %d", &l, &r);
printf("%d
", b[r] - b[l - 1]);
}
return 0;
}
二维前缀和
一维到二维需要由点延伸到面,一维的前缀和是起点到指定数的所有数的总和,那么二维的前缀和就是起点到该点矩阵内所有数的和。
要求
如下图所示,要求黑色部分的前缀和,将黑色部分顶点的值,也就是

设前缀和对应的数组为
得到每个数对应的前缀和后,要求
推导过程文章二维差分部分有详解,在此不多赘述。
代码模板
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1005;
int a[N][N], b[N][N];
int main() {
int n, m, q, x1, x2, y1, y2;
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]);
b[i][j] = a[i][j] + b[i][j - 1] + b[i - 1][j] - b[i - 1][j - 1];
}
}
while(q --) {
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d
", b[x2][y2] - b[x2][y1 - 1] - b[x1 - 1][y2] + b[x1 - 1][y1 - 1]);
}
return 0;
}
差分
引入
- 概述:差分为前缀和的逆运算。
- 解决的问题:对一个数组的一个区间统一做同一个处理。
- 方法:
使用改变序列第i 个数的大小,则序列从第i 个数开始的子序列总和(也就是前缀和)都会随之改变。
有一序列形如:
1 2 3 4 5 6
此时序列的前缀和序列:
1 3 6 10 15 21
现将第四个数4加一,变为5,此时序列:
1 2 3 5 5 6
对应前缀和变化:
1 3 6 11 16 22
可以看出,一个序列的第
那么根据这个性质,我们可以通过输出前缀和的方式,对i之后的区间统一加上k。
一维差分
我们看这样一道题目:
题目描述
输入一个长度为
接下来输入
[
l
,
r
]
[l, r]
[l,r]之间的每个数加上
请你输出进行完所有操作后的序列。
输入格式
第一行包含两个整数
第二行包含个整数,表示整数序列。
接下来
输出格式
共一行,包含几个整数,表示最终序列。
数据范围
1
≤
n
,
m
≤
100000
1le n, mle 100000
1≤n,m≤100000
1
≤
l
≤
r
≤
n
1le lle rle n
1≤l≤r≤n
?
1000
≤
c
≤
1000
-1000le cle 1000
?1000≤c≤1000
?
1000
≤
整数序列中元素的值
≤
1000
-1000le 整数序列中元素的值le 1000
?1000≤整数序列中元素的值≤1000
分析
根据引入部分,我们可以轻松推出要在
[
l
,
r
]
[l, r]
[l,r]之间的每个数加上
但由于只需要处理
图示

这个思路的核心思想就是,把给定序列当作一个序列的前缀和,根据该序列求出对应的原序列,对原序列进行变换,变换后对应的前缀和即为目标序列。
按照引入部分的例子,序列
1 2 3 4 5 6
为一个序列的前缀和,则其对应的原序列为:
1 1 1 1 1 1
如若需要进行的变换为将第三个到第五个数字都加一,按照之前的分析,需要把原序列的第三个数字(
1 1 2 1 1 0
变换后对应的前缀和为:
1 2 4 5 6 6
即目的序列。
代码模板
#include <iostream>
using namespace std;
int n, m, c, x, y;
const int N = 1e5 + 6;
int a[N], b[N]; //a数组存放给定序列,b数组存放差分。
void insert(int x, int y, int c) { // 写一个函数专门处理每一次变换
b[x] += c; // l处加c
b[y + 1] -= c; // r + 1处-c
}
int main() {
cin>> n >> m;
for(int i = 1; i <= n; i ++) {
scanf("%d", &a[i]);
insert(i, i, a[i]); // 求原序列过程即对[i, i]加a[i]
}
while(m --) {
scanf("%d%d%d", &x, &y, &c);
insert(x, y, c); // 进行变换
}
for(int i = 1; i <= n; i ++) {
b[i] += b[i - 1]; // 对原序列求前缀和并输出
printf("%d ", b[i]);
}
return 0;
}
二维差分
对应的二维题目:
题目描述
输入一个
每个操作都要将选中的子矩阵中的每个元素的值加上c。
请你将进行完所有操作后的矩阵输出。
输入格式
第一行包含整数
接下来
接下来
输出格式
共
数据范围
1
≤
n
,
m
≤
1000
1le n, mle 1000
1≤n,m≤1000
1
≤
q
≤
100000
1le qle 100000
1≤q≤100000
1
≤
x
1
≤
x
2
≤
1000
1le x_1le x_2le 1000
1≤x1?≤x2?≤1000
1
≤
y
1
≤
y
2
≤
1000
1le y_1le y_2le 1000
1≤y1?≤y2?≤1000
?
1000
≤
c
≤
1000
-1000le cle 1000
?1000≤c≤1000
?
1000
≤
矩阵内元素的值
≤
1000
-1000le 矩阵内元素的值le 1000
?1000≤矩阵内元素的值≤1000
题目分析
有了前面的一维差分的引入,导入二维差分就比较方便了,一维是线而二维就是面,操作也是一个面积一个面积的变化,故给定的是矩阵。
根据一维差分,我们同样把给定矩阵当作一个矩阵的前缀和,求出原矩阵后,对原矩阵进行操作。此时把二维的前缀和当作从(x, y)点到矩阵右下角的点的所有数的和。
当要对顶点为

图中绿色绿框代表的区域即
则目标红色区域的范围为:
代码模板
#include <iostream>
using namespace std;
const int N = 1005;
int a[N][N], b[N][N]; //a数组存放给定矩阵,b数组存放差分。
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() {
int n, m, c, q, x1, x2, y1, y2;
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 --) {
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][j] + b[i][j - 1] + b[i - 1][j] - b[i - 1][j - 1] ;
printf("%d ", b[i][j]);
}
puts("");
}
return 0;
}
课后练习:
ACwing一维前缀和模板题
ACwing二维前缀和模板题
ACwing寒假每日一题集训4967
ACwing一维差分模板题
ACwing二维差分模板题