5.前缀和

5.前缀和

核心思想

通过预处理生成前缀和数组,将 "区间和查询" 从 O (n) 优化为 O (1),适用于多次查询的场景。

1. 一维前缀和

定义

  • 原数组a[1..n](下标从 1 开始),前缀和数组s[1..n],其中s[i] = a[1] + a[2] + ... + a[i]
  • 区间和公式:sum(l..r) = s[r] - s[l-1](下标从 1 开始,避免l=1l-1=0的边界问题)。

代码模板(含应用)

C++ 复制代码
#include <cstdio>
using namespace std;

const int N = 1e5 + 10;
int a[N], s[N];

int main() 
{
    int n, m;// n个数,处理m次查询
    scanf("%d%d", &n,&m);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    // 预处理前缀和
    for (int i = 1; i <= n; i++) s[i] = s[i-1] + a[i];
    
    while (m--) 
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l-1]);
    }
    
    return 0;
}

测试案例

  • 输入:n=5,a=[1,2,3,4,5],m=2,查询 (1,3)、(2,5)
  • 预处理:s=[0,1,3,6,10,15]
  • 查询结果:6-0=6(1+2+3),15-1=14(2+3+4+5)
  • 输出:6、14

2. 二维前缀和(子矩阵和)

定义

  • 原矩阵a[1..n][1..m](下标从 1 开始),前缀和矩阵s[1..n][1..m],其中s[x][y]表示左上角 (1,1) 到右下角 (x,y) 的子矩阵和;
  • 预处理公式:s[x][y] = s[x-1][y] + s[x][y-1] - s[x-1][y-1] + a[x][y](避免重复计算重叠区域);
  • 子矩阵和公式:sum(x1,y1,x2,y2) = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]。

代码模板(含应用)

C++ 复制代码
#include <cstdio>
using namespace std;

const int N = 1e3 + 10;
int s[N][N]; // 直接用s存储原矩阵+前缀和(节省空间)

int main() 
{
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    // 读入原矩阵并预处理前缀和
    for (int i = 1; i <= n; i++) 
    {
        for (int j = 1; j <= m; j++) 
        {
            scanf("%d", &s[i][j]);
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];//求前缀和
        }
    }
    // 处理q次查询
    while (q--) 
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        int res = s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];//算矩阵和
        printf("%d\n", res);
    }
    return 0;
}

测试案例

  • 输入矩阵:

    1 2 3

    4 5 6

    7 8 9

  • 预处理 s 矩阵(部分):s [2][2] = 1+2+4+5=12,s [3][3] = 45

  • 查询子矩阵 (1,1,2,2):12,查询 (2,2,3,3):5+6+8+9=28

  • 输出:12、28