前缀和、差分

及不同区间操作比较

Posted by XDong on March 14, 2021

前缀和、差分

前缀和

前缀和是一种重要的预处理,能大大降低查询的时间复杂度。可以简单理解为“数列的前n项的和”

  • 区间求和复杂度为$O(1)$
1
2
a[i]: 1  2  3  4  5  //原始数组
b[i]: 1  3  6  10 15 // 前缀和数组

代码:

1
2
3
4
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    b[i] = a[i] + a[i - 1];
  }

二维前缀和

$sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j]$

最大子序和

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <cstring>
#include <iostream>
using namespace std;

int sum[int(3e5 + 5)];
int main() {
  int n, m, num, res = 0;
  memset(sum, 0, sizeof(sum));
  cin >> n >> m;
  for (int i = 1; i <= n; i++) {
    cin >> num;
    sum[i] = sum[i - 1] + num;
    for (int j = 1; j <= m; j++) {
      if (i >= j) {
        res = max(res, sum[i] - sum[i - j]);
      }
    }
  }
  cout << res << endl;
  return 0;
}

差分

差分是一种和前缀和相对的策略,可以当做是求和的逆运算,指的就是当前值与前一个值的差值

  • 区间加减复杂度为$O(1)$
1
2
a[i]: 1  2  3  4  5  //原始数组
b[i]: 1  1  1  1  1  //差分数组

代码:

1
2
3
4
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    b[i] = a[i] - a[i - 1];
  }

一维差分加减

1
2
3
4
void insert(int l, int r, int c) {
  b[l] += c;
  b[r + 1] -= c;
}

二维差分加减

$b[i][j] = a[i][j] - a[i - 1][j] - a[i][j - 1] + a[i - 1][j - 1]$

1
2
3
4
5
6
void insert(int x1, int y1, int x2, int y2, int c) {
  b[x1][y1] += c;
  b[x1][y2 + 1] -= c;
  b[x2 + 1][y1] -= c;
  b[x2 + 1][y2 + 1] += 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 <bits/stdc++.h>
#define IO ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;

const int MAXN = 1e5 + 5;
int a[MAXN], b[MAXN];

void insert(int l, int r, int c) {
  b[l] += c;
  b[r + 1] -= c;
}

int main() {
  IO;
  int n, m;
  cin >> n >> m;
  for (int i = 1; i<= n; i++) {
    cin >> a[i];
    b[i] = a[i] - a[i - 1];
  }
  for (int i = 1; i <= m; i++) {
    int l, r, n;
    cin >> l >> r >> n;
    insert(l, r, n);
  }
  int res = 0;
  for (int i = 1; i <= n; i++) {
    res += b[i];
    cout << res << " ";
  }
  return 0;
}

不同区间操作总结分析

前缀和

  • 区间求和$O(1)$
  • 单点修改$O(n)$

差分

  • 区间修改$O(1)$
  • 单点查询$O(n)$

树状数组

  • 单点修改$O(logn)$
  • 区间修改$O(logn)$
  • 单点查询$O(logn)$
  • 区间求和$O(logn)$

线段树(树状数组拓展版)

  • 单点修改$O(logn)$
  • 区间修改$O(logn)$
  • 单点查询$O(logn)$
  • 区间求和$O(logn)$
  • 区间最大值$O(logn)$
  • 区间最小值$O(logn)$

参考链接