一维前缀和与二维前缀和的详细用法和介绍
一、一维前缀和
- 定义
- 对于数组(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)); // 输出: 9 return 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)); // 输出: 28 return 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))。