一、前缀和简介
前缀和是一种能够在O(n)的预处理,O(1)的查询每段区间的和的一个算法。
考试中,前缀和是一种能够降低复杂度的好东西。
二、前缀和算法原理
思路
我们用数组s,是的每一个si都满足si=si−1+ai。
也就是说,s数组的每一个位置i都表示前i个数的和,故叫前缀和。
但是我们要求一段区间的和该怎么办呢?
其实可以用减去多余的部分来求出答案。
比如我们要求区间[l,r]的和,假设我们已经算好s数组了。
我们先看一张图:
这里我们的和只要红色部分减去绿色部分,红色部分是sr,绿色部分是sl−1,所以suml,r=sr−sl−1。
代码
#include <iostream>
using namespace std;
const int N = 100010;
int n,m;
int a[N];
int s[N];
int main () {
cin >> n >> m;
for (int i = 1;i <= n;i++) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
while (m--) {
int l,r;
cin >> l >> r;
cout << s[r] - s[l - 1] << endl;
}
return 0;
}
思路
这里我们讲一下二维前缀和。
我们用si,j表示(i,j)左上角的所有点(包括他自己)的和。
那我们该怎么求s数组呢?
先看一张图:
这里的si,j就是绿色部分+蓝色部分−紫色部分+当前数。
所以si,j=si−1,j+si,j−1−si−1,j−1+ai,j。
那我们要求任意一个部分的和该怎么求呢?
其实也很简单,我们对着上面那的图,就是把蓝色紫色绿色部分的位置改一下。
比如我们要求(x1,y1)到(x2,y2)的和,那么这一段的和就是sx2,y2−sx1−1,y2−sx2,y1−1+sx1−1,y1−1
代码
#include <iostream>
using namespace std;
const int N = 1010;
int n,m;
int a[N][N];
int s[N][N];
int main () {
cin >> n >> m;
for (int i = 1;i <= n;i++) {
for (int j = 1;j <= n;j++) {
cin >> a[i][j];
s[i][j] = s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1] + a[i][j];
}
}
while (m--) {
int x1,y1,x2,y2;
cin >> x1 >> y1 >> x2 >> y2;
cout << s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1] << endl;
}
return 0;
}
二维前缀和里面其实有个小小的容斥原理hh
嗯
没人不是没有原因的