一、一维前缀和
- 定义
- 对于数组(a),其前缀和数组(s)定义为(s[i]=a+a[1]+... +a[i]),主要用途是快速计算数组中任意区间的和。
- 用法
- 初始化前缀和数组
- 代码实现:
int a[N]; // 原数组 int s[N]; // 前缀和数组 s[0]=a[0]; for (int i = 1; i < N; i++) {s[i]=s[i - 1]+a[i]; }
- 代码实现:
- 查询区间和
- 若查询区间([l,r])的和,可使用(s[r]-s[l - 1])。若(l == 0),则直接返回(s[r])。
- 代码实现:
int query(int l, int r) {if (l == 0) return s[r];return s[r]-s[l - 1]; }
- 初始化前缀和数组
- 示例
- 代码示例:
#include <stdio.h> #define N 5 int main() {int a[N]={1,2,3,4,5};int s[N];s[0]=a[0];for (int i = 1; i < N; i++) {s[i]=s[i - 1]+a[i];}printf("Sum from index 1 to 3: %d\n", query(1,3)); // 输出: 9return 0; }
- 代码示例:
二、二维前缀和
- 定义
- 对于二维数组(a),其前缀和数组(s)定义为(s[i][j]=a[0][0]+..... +a[i][j]),主要用途是快速计算二维数组中任意子矩阵的和。
- 用法
- 初始化前缀和数组
- 代码实现:
int a[N][M]; // 原二维数组 int s[N][M]; // 前缀和数组 s[0][0]=a[0][0]; for (int i = 1; i < N; i++) {s[i][0]=s[i - 1][0]+a[i][0]; } for (int j = 1; j < M; j++) {s[0][j]=s[0][j - 1]+a[0][j]; } for (int i = 1; i < N; i++) {for (int j = 1; j < M; j++) {s[i][j]=s[i - 1][j]+s[i][j - 1]-s[i - 1][j - 1]+a[i][j];} }
- 代码实现:
- 查询子矩阵和
- 若查询子矩阵((x1,y1))到((x2,y2))的和,可使用(s[x2][y2]-s[x1 - 1][y2]-s[x2][y1 - 1]+s[x1 - 1][y1 - 1])。若(x1 == 0)或(y1 == 0),则需特殊处理。
- 代码实现:
int query(int x1, int y1, int x2, int y2) {if (x1 == 0 && y1 == 0) return s[x2][y2];if (x1 == 0) return s[x2][y2]-s[x2][y1 - 1];if (y1 == 0) return s[x2][y2]-s[x1 - 1][y2];return s[x2][y2]-s[x1 - 1][y2]-s[x2][y1 - 1]+s[x1 - 1][y1 - 1]; }
- 初始化前缀和数组
- 示例
- 代码示例:
#include <stdio.h> #define N 3 #define M 3 int main() {int a[N][M] = {{1,2,3},{4,5,6},{7,8,9}};int s[N][M];s[0][0]=a[0][0];for (int i = 1; i < N; i++) {s[i][0]=s[i - 1][0]+a[i][0];}for (int j = 1; j < M; j++) {s[0][j]=s[0][j - 1]+a[0][j];}for (int i = 1; i < N; i++) {for (int j = 1; j < M; j++) {s[i][j]=s[i - 1][j]+s[i][j - 1]-s[i - 1][j - 1]+a[i][j];}}printf("Sum from (1,1) to (2,2): %d\n", query(1,1,2,2)); // 输出: 28return 0; }
- 代码示例:
s[i][j]=s[i - 1][j]+s[i][j - 1]-s[i - 1][j - 1]+a[i][j];此代码可转化为子矩阵面积来理解
三、总结
- 一维前缀和:用于快速计算数组中任意区间的和。
- 二维前缀和:用于快速计算二维数组中任意子矩阵的和。
- 这两种前缀和技巧在处理区间和问题时非常高效,能够将时间复杂度从(O(n))或(O(n^{2}))降低到(O(1))。