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