前缀和与差分(持续更新中~)
目录
- 一维前缀和
- 二维前缀和
- 一维差分
- 二维差分
前言
- 差分与前缀和联系比较紧密,如一个数组x的差分数组的前缀和数组等于x本身。博主在解题过程多次遇到这个知识点,作为基础算法之一,这个知识点要求重点掌握。
- 一个看法:前缀和数组单个元素代表是原数组左边或左上所有元素之和、单个差分数组元素修改是修改原数组右边或右下方所有元素的值.(均包含当前元素本身)
1 一维前缀和
- 主要功能:
1.快速查询任意连续区间元素之和- 时间复杂度:
构建o(n)、查询o(1)- 基本思路:
1.利用原数组构建一个前缀和数组:s[i] = s[i - 1] + a[i]
2.查询[l, r]区间元素和时答案为:s[r] - s[l - 1]- 注意事项:
1.因为可能要用到s[最小下标-1](构建s[最小下标]时,查询区间左端点为原数组起点时),所以s数组的下标最好从1开始,为了一一对应,原数组的下标最好也从1开始。- 经典例题:
1.模板题:AcWing 795. 前缀和
//模板代码示例
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int s[N];
int main()
{
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> s[i]; //该题原数组无需保存
s[i] += s[i - 1]; //构建前缀和数组,
}
while(m--)
{
int l, r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl; //区间元素之和查询
}
return 0;
}
2 二维前缀和
- 主要功能:
1.快速查询任意子矩阵元素之和- 时间复杂度:
构建o(n2)、查询o(1)- 基本思路:
1.构建二维前缀和数组:s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j]
2.查询以(x1,y1)为左上角端点,(x2,y2)为右下角端点的子矩阵之和:ans = s[x2][y2] - s[x1-1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]- 注意事项:
1.注意边界问题
2.原数组和前缀和数组的下标最好从1开始- 经典例题:
1.模板题:AcWing 796. 子矩阵的和- 拓展练习:
1.Acwing99.激光炸弹
2.Acwing1230.K倍区间
//模板代码示例
#include<iostream>
using namespace std;
const int N = 1005;
int a[N][N], s[N][N], n, m, q;
//查询子矩阵和函数
int query(int x1, int y1, int x2, int y2)
{
return s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1];
}
int main()
{
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];
while(q--)
{
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << query(x1, y1, x2, y2) << endl;
}
return 0;
}
3 一维差分
- 主要功能:
1.快速对任意连续区间所有元素加减一个数x- 时间复杂度:
构建o(n),修改o(1),还原o(n)- 基本思路:
1.构建差分数组:b[i] = a[i] - a[i - 1](类比一维前缀和数组构建公式,只是a[]移到等号左边,所有s[]移到等号右边)
2.修改区间[l,r],使所有元素加上x:b[l] += x, b[r + 1] -= x
3.将差分数组还原为原数组a[]:a[i] = a[i - 1] + b[i](和一维前缀和数组的构建公式原理一样)- 注意事项:
1.下标最好从1开始- 经典例题:
1.模板题:AcWing 797. 差分- 拓展练习:
1.差分数组性质:Acwing100. 增减序列
2.区间修改:Acwing101.最高的牛
//模板代码示例
#include<iostream>
using namespace std;
const int N = 1e5 + 5;
int a[N], b[N], n, m;
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)
{
cin >> a[i];
b[i] = a[i] - a[i - 1]; //构建差分数组
}
while(m--)
{
int l, r, c;
cin >> l >> r >> c;
b[r + 1] -= c; //区间修改
b[l] += c;
}
for(int i = 1; i <= n; i++)
b[i] += b[i - 1]; //将差分数组还原为原数组
for(int i = 1; i <= n; i++)
cout << b[i] << " ";
return 0;
}
4 二维差分
- 功能:
1.快速对一个子矩阵所有元素加减一个数x- 时间复杂度:
构建o(n2), 修改o(1), 还原o(n2)- 基本思路:
1.构建二维差分矩阵:b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1](类比于二维前缀和数组的构建公式,只是将a[]移到等式左边,将所有s[]移到右边)
2.(x1,y1)和(x2,y2)为左上和右下端点的子矩阵所有元素加x:b[x1][y1]+=x, b[x1][y2+1]-=x, b[x2+1][y1]-=x, b[x2+1][y2+1]+=x
3.还原:a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j](类比二维前缀和构建公式)- 注意事项:
1.下标从1开始- 经典例题:
1.模板题:AcWing 798. 差分矩阵
#include<iostream>
using namespace std;
const int N = 1e3 + 5;
int a[N][N], b[N][N];
int n, m, q;
int main()
{
cin >> n >> m >> q;
//构建差分矩阵
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
{
cin >> a[i][j];
b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1];
}
//对子矩阵元素加c
while(q--)
{
int x1, x2, y1, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
b[x1][y1] += c;
b[x1][y2 + 1] -= c;
b[x2 + 1][y1] -= c;
b[x2 + 1][y2 + 1] += c;
}
//还原
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];
//输出答案
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
cout << a[i][j] << " ";
cout << endl;
}
return 0;
}
PS:文章如有问题恳请各位大佬帮忙指正,本文章会持续不定期更新中~