一、前缀和
什么是前缀和?
设原数列A[]:a1、a2、a3 ……
前缀和数列 si = a1 + a2 + …… ai (i=0、1、2、3……) (注意 让s0 = 0)
1、怎么求前缀和?
for (int i = 1; i <= n; i ) cin >> a[i];
for (int i = 1; i <= n; i ) s[i] = s[i - 1] + a[i];
2、前缀和有什么作用?求A数列中一段区间[l, r]的元素值之和 = s[r] - s[l-1]
比如求a4 + a5 + …… a9,用前缀和来求就是 s9 - s3
前缀和的好处:如果用正常方法求数组A在[l,r]的和 时间复杂度O(n)
用前缀和来求,s[r] - s[l-1],O(1)
795.前缀和
输入一个长度为 n的整数序列。
接下来再输入 m个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l个数到第 r个数的和。
输入格式:
第一行包含两个整数 n和 m。
第二行包含 n个整数,表示整数数列。
接下来 m行,每行包含两个整数 l和 r,表示一个询问的区间范围。
输出格式:
共 m行,每行输出一个询问的结果。
#include <iostream>
using namespace std;
const int N = 100000010;
int a[N];
int s[N]; //表示前缀和
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++) s[i] = s[i - 1] + a[i];
int l, r;
while(m --)
{
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
}
** 二维前缀和
一维前缀和用于求某一段区间的和,二维前缀和用于求某一个子矩阵里的和
S[i][j]表示的是左上角所有元素的和,S[1][1]表示最左上角的一个格子
s[0][?] = 0 s[?][0] = 0
1、二维前缀和Sij怎么求?
2、在已知所有的Sij的情况下,怎么求某一个子矩阵里的和(下图中蓝色小正方形里面所有元素的和)呢?
ACWing796. 子矩阵的和
输入一个 n行 m列的整数矩阵,再输入q个询问,每个询问包含四个整数x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式:
第一行包含三个整数 n,m,q。
接下来 n行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。
输出格式:
共q行,每行输出一个询问的结果。
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N], s[N][N];
int main()
{
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> a[i][j];
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
s[i][j] = a[i][j] + s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1]; // 求二维前缀和
int x1, y1, x2, y2;
while (q --)
{
cin >> x1 >> y1 >> x2 >> y2;
// 用二维前缀和来求子矩阵中的元素和
cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
}
}
二、差分
差分是前缀和的逆运算
定义:
给定原数组a:a1、a2、…… an
构造b数组 b1、b2 …… bn (满足b1+b2+……+bi=ai)
b数组称为a数组的差分
1、差分的作用:如果要实现给定一个区间[l,r], a数组中此区间中的数都+c
遍历方式:O(n) 差分:O(1)
差分的实现方式: b[l]+c b[r+1]-c
最后,求一遍b数组的前缀和即可得到[l,r]都加了c的 a数组
2、差分数组b[i]的初始构造
假设一开始a数组中的元素都为0,则差分数组b数组自然也都为0。
通过操作差分b数组,来给a数组的每一个区间[i,i]+a[i],在这个过程中等同于完成了差分数组b数组的构建。
797.差分
输入一个长度为 n的整数序列。
接下来输入 m个操作,每个操作包含三个整数l,r,c,表示将序列中[l,r]之间的每个数加上 c。
请你输出进行完所有操作后的序列。
输入格式:
第一行包含两个整数 n和 m。
第二行包含 n个整数,表示整数序列。
接下来 m行,每行包含三个整数 l,r,c, 表示一个操作。
输出格式:
共一行,包含 n个整数,表示最终序列。
#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N]; //b是差分数组
// 作用:给区间[l,r]的a数组的每个元素+c
void insert(int l, int r, int c)
{
b[l] += c;
b[r + 1] -= c;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i ++) cin >> a[i];
/* 差分数组b数组的初始构建
假设一开始a数组全是0,则自然b数组也全是0,此时通过操作差分b数组,来给a数组的每一个区间[i,i]+a[i],
在这个过程中自然而然地便完成了b数组的初始构建
*/
for (int i = 1; i <= n; i ++) insert(i, i, a[i]);
int l, r, c;
while( m -- )
{
cin >> l >> r >> c;
insert(l, r, c);
}
for (int i = 1; i <= n; i ++) b[i] += b[i - 1]; //用b数组自身来计算前缀和(不再用a数组表示前缀和)
for (int i = 1; i <= n; i ++) cout << b[i] << " ";
}
** 二维差分
为了解决二维数组b[][]的一个子矩阵都加上c的问题,我们人为构造一个二维差分数组:b[i][j] ,差分数组满足:b[i][j]所有左上角的元素之和 = a[i][j]
1、二维差分数组b[i][j]的作用:
实现二维数组a[i][j]中 从x1,y1 到 x2,y2 的子矩阵中的所有数+c
实现方式:b[x1][y1]+c, b[x2+1][y1]-c, b[x1][y2+1]-c, b[x2+1][y2+1]+c
[注] b[x][y]+c的作用是:a[x][y]的右下角的矩阵中的所有元素+c
2、差分数组b[i][j]的初始构建
假设一开始a[][]数组中的元素都为0,则差分数组b[][]数组中的元素自然也都为0。
此时通过操作差分b数组,来给a数组的每一个左上角[i,j],右下角[i,j]的子矩阵(也即a[i][j]元素本身) + a[i][j],在这个过程中等同于完成了差分数组b数组的构建。
AcWing 798.差分矩阵
输入一个 n行 m列的整数矩阵,再输入q个操作,每个操作包含五个整数x1,y1,x2,y2,c,其中 (x1,y1)和 (x2,y2)表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。
输入格式:
第一行包含整数 n,m,q。
接下来 n行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含 5个整数 x1,y1,x2,y2,c,表示一个操作。
输出格式:
共n行,每行 m个整数,表示所有操作进行完毕后的最终矩阵。
#include <iostream>
using namespace std;
const int N = 1010;
int a[N][N], b[N][N], s[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()
{
int n, m, q;
cin >> n >> m >> q;
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
cin >> a[i][j];
/* 差分数组的初构建
假设一开始a[][]数组中的元素都为0,则差分数组b[][]数组中的元素自然也都为0。
此时通过操作差分b数组,来给a数组的每一个左上角[i,j],右下角[i,j]的子矩阵(也即a[i][j]元素本身)+a[i][j],
在这个过程中等同于完成了差分数组b数组的构建。
*/
for (int i = 1; i <= n; i ++)
for (int j = 1; j <= m; j ++)
insert(i, j, i, j, a[i][j]);
int x1, y1, x2, y2, c;
while ( q -- )
{
cin >> x1 >> y1 >> x2 >> y2 >> c;
// 用差分来实现一块矩阵中的元素+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];
cout << b[i][j] << " ";
}
cout << endl;
}
}