一维前缀和中sum[i]表示前i个元素(arr[0]到arr[i-1])的和,即sum[0]=0;二维同理,sumi表示从(0,0)到(i-1,j-1)的子矩阵和,均需n+1/m+1大小避免越界。

一维前缀和:用 vector 预处理,sum[i] 表示前 i 项和还是前 i+1 项?
关键在索引对齐——绝大多数人写错就卡在这一步。标准做法是让 sum[0] = 0,sum[i] 表示原数组 arr[0] 到 arr[i-1] 的和(即前 i 个元素)。这样区间 [l, r] 的和就是 sum[r+1] - sum[l],不用特判边界。
常见错误现象:sum[r] - sum[l-1] 导致越界或漏算第一个元素;或者初始化 sum[0] = arr[0] 后强行套公式,结果 l=0 时访问 sum[-1]。
- 初始化:先
sum.resize(n + 1),再循环i从1到n,赋值sum[i] = sum[i-1] + arr[i-1] - 查询:闭区间
[l, r](0-indexed)对应sum[r+1] - sum[l] - 别用
int sum[n+1]在栈上开大数组——n 大时可能栈溢出,一律用vector
二维前缀和:sum[i][j] 的定义必须包含左上角 (0,0) 吗?
必须。二维前缀和本质是容斥,sum[i][j] 必须定义为从 (0,0) 到 (i-1,j-1) 子矩阵的和(即前 i 行、前 j 列),否则容斥公式 sum[r2+1][c2+1] - sum[r1][c2+1] - sum[r2+1][c1] + sum[r1][c1] 不成立。
使用场景:频繁查矩形区域和,比如图像积分图、子矩阵最大和预处理。
立即学习“C++免费学习笔记(深入)”;
- 构建时循环从
i=1,j=1开始,sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + mat[i-1][j-1] - 查
[r1, r2] × [c1, c2](全闭,0-indexed):结果是sum[r2+1][c2+1] - sum[r1][c2+1] - sum[r2+1][c1] + sum[r1][c1] - 别省略
+1偏移——哪怕只查单点(i,j),也要写成sum[i+1][j+1] - sum[i][j+1] - sum[i+1][j] + sum[i][j]
修改操作来了怎么办?原生前缀和还顶得住吗?
顶不住。update(i, delta) 会破坏所有依赖 arr[i] 的前缀和项,朴素重算复杂度 O(n),失去预处理意义。
这时候该换数据结构:单点改 + 区间查,std::vector 前缀和就不合适了,得上 fenwick_tree(树状数组)或 segment_tree(线段树)。
- 如果只有批量建表 + 大量查询,坚持用前缀和,别硬加 update
- 如果真要动态改,用
fenwick_tree:代码短、常数小、支持单点改 + 前缀查,再组合出区间查 - 别试图在原前缀和数组上做“局部修复”——逻辑绕、易错,且没性能优势
边界检查和数据类型:为什么算着算着变成负数或极大值?
两个坑最隐蔽:一是下标越界读了未初始化内存(尤其二维 sum 开 [n][m] 却按 n+1 行访问),二是整数溢出——前缀和增长快,int 很容易爆。
错误信息如 AddressSanitizer: heap-buffer-overflow 或结果异常大/负,大概率是这两个问题。
- 二维
sum至少开(n+1) × (m+1),别少加 1 - 原始数组元素范围若达
1e5,长度1e5,前缀和峰值可达1e10,必须用long long - 调试时打一行
cout 看前几项是否符合预期,比瞎猜快得多
实际写的时候,最容易被忽略的是二维容斥公式的四个角偏移量——它不像一维那样直觉,每次手写都值得默念一遍:右下、左下、右上、左上,对应的是 r2+1,c2+1、r1,c2+1、r2+1,c1、r1,c1。










