前缀和与差分算法模版

前缀和与差分

前缀和与差分互为逆运算,本质上是一种思想而不是一种模版,对于一段问题的求解有奇效

前缀和

  1. 一维前缀和:给定一个数组,来查询[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. 二维前缀和:与一维很相似,只是表示的变成一块区域[左上, 右下]的矩形内部所有元素的和
 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;
}
0%