一维前缀和
对于一个数组a[ ],我们构造一个数组s[ ],使得:
s
[
i
]
=
∑
k
=
1
i
a
[
k
]
s[i] = \sum_{k = 1}^i a[k]
s[i]=k=1∑ia[k]
1. 构造前缀和
for(int i = 1; i <= n; i ++ ) {
cin >> a[i];
s[i] = s[i - 1] + a[i];
}
2. 求[l, r]的和
cout << s[r] - s[l - 1] << endl;
二维前缀和
对于一个数组a[ ][ ],我们构造一个数组s[ ][ ],使得:
s
[
i
]
[
j
]
=
∑
k
1
=
1
i
∑
k
2
=
1
j
a
[
k
1
]
[
k
2
]
s[i][j] = \sum_{k_1 = 1}^i\sum_{k_2 = 1}^j a[k_1][k_2]
s[i][j]=k1=1∑ik2=1∑ja[k1][k2]
这样可能不是很直观,我们用一个图形来表示:
图中绿色部分即为s[i][j]
1. 构造前缀和
由于构造前缀和是一个从上到下,从左到右的一个过程。所以对于s[i][j]来说,s[i - 1][j]和s[i][j - 1]已经被构造过了,我们可以利用已有结果去构造s[i][j]:
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= m; j ++ ) {
cin >> a[i][j];
// s[i - 1][j - 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),求该子矩阵的和。其中(x1, y1)为矩阵左上角坐标,(x2, y2)为矩阵右下角坐标。
由图我们可以看出:
// 红色部分被减了两次,所以要加上一次
cout << s[x2][y2] - s[x2, y1 - 1] - s[x1 - 1, y2] + s[x1 - 1][y1 - 1] << endl;
圆上前缀和
圆上前缀和需要用到破环成链的思想(参考AcWing y总)。破环成链的思路就是,把圆形变成2个线段,长度相同,元素相同。
一般来说,题目会给出圆上的 n n n个点,比如 a [ 0 ] , a [ 1 ] , . . . , a [ n − 1 ] a[0], a[1], ..., a[n - 1] a[0],a[1],...,a[n−1]。首先我们要做的就是把 a a a数组复制一倍,即:
const int N = xxx;
int a[2 * N];
for(int i = 0; i < n; i ++ ) {
cin >> a[i];
a[i + n] = a[i];
}
由于复制了一倍,所以数组大小也要开成 2 ∗ n 2 * n 2∗n。
相较于一维前缀和,圆上前缀和就是多了这个预处理工作,后续的求前缀和、求区间和都是一样的。