前缀和可以用于快速求某一个区间各个值的和,原理是先预处理好所有的值,创建一个预处理好的数组,这个预处理的数组中的每个值都是原数组相同位置上前面的所有值相加。这是一种空间换取时间的做法。
1.一维前缀和
1.1【模板】前缀和
假设数组 f[N] 为我们的预处理数组,a[N] 为我们的原数组, 那创建预数组的方式为:
cpp
f[i] = f[i - 1] + a[i];
有时候我们并不一定要保留原来值,只要能创建出预数组就可以;
想要还原出区别和也非常简单,假设 l 为左区间, r 为又区间,那这个区别的和为:
cpp
f[r] - f[l - 1]
这就是前缀和的核心代码,这个算法主要是和别的算法一起搭在一起考察的:
cpp
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
typedef long long LL;
int n, m;
LL f[N];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
LL x; cin >> x;
f[i] = f[i - 1] + x;
}
while (m--)
{
LL l, r; cin >> l >> r;
cout << f[r] - f[l - 1] << endl;
}
return 0;
}
1.2最大子段和
这道题也可以使用前缀和来解决,因为我们要想找出最大的区间,我们是想要让减去的后面那段尽可能的小,所以我们可以定义一个变量premin使得减去前面的区间最小:
cpp
#include <iostream>
using namespace std;
const int N = 2e5 + 10;
int f[N];
int n;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int x; cin >> x;
f[i] = f[i - 1] + x;
}
int ret = -1e5;
int premin = 0;
for (int i = 1; i <= n; i++)
{
ret = max(ret, f[i] - premin);
premin = min(premin, f[i]);
}
cout << ret << endl;
return 0;
}
2.二维前缀和
2.1【模板】二维前缀和
二维前缀和与一维前缀和行驶的功能是完全一样的,只不过将一段扩展成了一个二维空间。
二维前缀和的预处理和还原方式与一维有些不同,但我们也是可以推出来的
我们设垂直方向为i,水平方向为j,可以得到:

更大的区别正是由这样一个个的子问题推导出来的,所以二维前缀和的公式可以为:
cpp
f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + x
还原的推理公式也是同理我这里就不再赘述了
cpp
#include <iostream>
using namespace std;
const int N = 1010;
typedef long long LL;
int n, m, q;
LL f[N][N];
LL a[N][N];
int main()
{
cin >> n >> m >> q;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
LL x; cin >> x;
f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + x;
}
}
while (q--)
{
LL x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
LL ret = f[x2][y1 - 1] + f[x1 - 1][y2] - f[x1 - 1][y1 - 1];
cout << f[x2][y2] - ret << endl;
}
return 0;
}
2.2激光炸弹
这道题是二维前缀和的经典运用,我们可以通过m计算出这个区间,注意边界问题即可:
cpp
#include <iostream>
using namespace std;
const int N = 5010;
int a[N][N];
int f[N][N];
int n, m;
int main()
{
cin >> n >> m;
for (int i = 0; i < n; i++)
{
int x, y, v; cin >> x >> y >> v;
x++; y++;
a[x][y] += v;
}
for (int i = 1; i < N; i++)
{
for (int j = 1; j < N; j++)
{
f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + a[i][j];
}
}
int ret = 0;
m = min(m, N);
for (int i = m; i < N; i++)
{
for (int j = m; j < N; j++)
{
int x2, y2; x2 = i; y2 = j;
int x1, y1; x1 = i - m + 1; y1 = j - m + 1;
int t = f[x2][y2] - f[x2][y1 - 1] - f[x1 - 1][y2] + f[x1 - 1][y1 - 1];
ret = max(ret, t);
}
}
cout << ret << endl;
return 0;
}
完