前缀和与差分
前缀和与差分互为逆运算,本质上是一种思想而不是一种模版,对于一段问题的求解有奇效
前缀和
- 一维前缀和:给定一个数组,来查询
[l, r]
中所有数字的和,一共查询m
次
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
vector<int> nums(n, 0);
for (int i = 0; i < n; ++i) cin >> nums[i];
nums.insert(nums.begin(), 0);
vector<int> sums(n + 1, 0);
for (int i = 1; i <= n; ++i) {
sums[i] = sums[i - 1] + nums[i];
}
while (m--) {
int l, r;
cin >> l >> r;
cout << sums[r] - sums[l - 1] << endl;
}
return 0;
}
|
- 二维前缀和:与一维很相似,只是表示的变成一块区域
[左上, 右下]
的矩形内部所有元素的和
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
// 获取前缀和的模版
int get_sum(vector<vector<int>>& sums, int x, int y) {
int m = sums.size(), n = sums[0].size();
if (x < 0 || y < 0 || x >= m || y >= n) return 0;
return sums[x][y];
}
// 求区域和的模版
int get(vector<vector<int>>& sums, int x1, int y1, int x2, int y2) {
return get_sum(sums, x2, y2) - get_sum(sums, x2, y1 - 1) - get_sum(sums, x1 - 1, y2) + get_sum(sums, x1 - 1, y1 - 1);
}
int main()
{
int m, n, q;
cin >> m >> n >> q;
vector<vector<int>> nums(m, vector<int> (n, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> nums[i][j];
}
}
// 构造前缀和的模版
vector<vector<int>> sums(m, vector<int>(n, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
sums[i][j] = get_sum(sums, i - 1, j) + get_sum(sums, i, j - 1) - get_sum(sums, i - 1, j - 1) + nums[i][j];
}
}
while (q--) {
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
// x1--, y1--, x2--, y2--;
cout << get(sums, x1, y1, x2, y2) << endl;
}
return 0;
}
|
差分
对于数组$A_n = [a_1, a_2, a_3, … , a_n]$,构造数组$B_n = [b_1, b_2, b_3, …, b_n]$使得$a_i = b_1 + b_2 + b_3 + … + b_i$,换言之使得$A_n$数组是$B_n$数组的前缀和
对于一维数组的构造:(构造本身没那么重要,理解他俩的关系即可,因为对于所有的原始数组都可以认为本来是0,然后里面的初始值是影响)
$b_1 = a_1$
$b2 = a_2 - a_1$
$b_3 = a_3 - a_3$
…
$b_4 = a_4 - a_3$
对于这样的数组$B_n$就称数组$B_n$是数组$A_n$的差分,大多数时候是通过数组$B_n$来求数组$A_n$,一般是利用$B_n$对数组$A_n$的影响来解题,比如对于一段数组希望在[l, r]
这个区间内所有的数字加上C
,则对于差分数组$B_n$对l
进行加C
操作,而对r + 1
执行-C
再对差分数组求前缀和,得到的结果就是全部加上了C
,使用这个算法可以对多重影响一起求解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
int n, m;
// n个数字,m行操作
cin >> n >> m;
vector<int> nums(n + 1, 0);
vector<int> diff(n + 1, 0);
for (int i = 1; i <= n; ++i) {
cin >> nums[i];
diff[i] += nums[i];
diff[i + 1] -= nums[i];
}
// m行操作,对[l, r]区间内的所有数字进行加C的操作
while (m--) {
int l, r, c;
cin >> l >> r >> c;
diff[l] += c;
diff[r + 1] -= c;
}
for (int i = 1; i <= n; ++i) {
diff[i] += diff[i - 1];
cout << diff[i] << ' ';
}
cout << endl;
return 0;
}
|
对于二维数组来说也是一样的,是给一个子矩阵给上一个值(一个影响)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
void insert(vector<vector<int>>& sums, int x1, int y1, int x2, int y2, int c) {
sums[x1][y1] += c;
sums[x1][y2 + 1] -= c;
sums[x2 + 1][y1] -= c;
sums[x2 + 1][y2 + 1] += c;
}
int main() {
int m, n, q;
cin >> m >> n >> q;
vector<vector<int>> nums(m, vector<int> (n, 0));
vector<vector<int>> sums(m + 2, vector<int> (n + 2, 0));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
cin >> nums[i][j];
insert(sums, i + 1, j + 1, i + 1, j + 1, nums[i][j]);
}
}
while (q--) {
int x1, y1, x2, y2, c;
cin >> x1 >> y1 >> x2 >> y2 >> c;
insert(sums, x1, y1, x2, y2, c);
}
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
sums[i][j] += sums[i][j - 1] + sums[i - 1][j] - sums[i - 1][j - 1];
cout << sums[i][j] << ' ';
}
cout << endl;
}
return 0;
}
|