前缀和数组应初始化为长度n+1、prefix[0]=0,再用prefix[i+1]=prefix[i]+arr[i]递推;若用std::partial_sum需配合resize和迭代器偏移,手写循环更可控。

前缀和数组怎么初始化才不会越界
初始化前缀和数组时,常见错误是把 prefix[0] 直接赋成原数组第一个元素,然后从下标 1 开始累加——这会导致后续查询区间 [0, i] 时多算或少算一个值。正确做法是让 prefix 比原数组多一个位置,prefix[0] = 0,再用 prefix[i+1] = prefix[i] + arr[i]。
这样设计的好处是:任意区间 [l, r](闭区间)的和可统一写成 prefix[r+1] - prefix[l],无需特判左端点为 0 的情况。
- 如果原数组长度为
n,前缀和数组长度必须是n+1 - 不要用
vector<int> prefix(n)</int>然后填满,容易在r == n-1时访问prefix[n]越界 - 初始化必须用循环或
std::partial_sum,手写时注意索引偏移
用 std::partial_sum 还是手写循环
std::partial_sum 是标准库提供的安全、简洁方案,但默认行为是“原地覆盖”或“输出到另一段内存”,且起始值不自动补 0。直接用它生成带哨兵零的前缀和,得配合 std::vector resize 和迭代器偏移。
手写循环更可控,尤其适合嵌入式或性能敏感场景;而 std::partial_sum 在可读性和避免 off-by-one 错误上更有优势。
- 手写示例:
vector<long long> prefix(n + 1, 0); for (int i = 0; i </long>
-
std::partial_sum示例:vector<long long> prefix(n + 1, 0); partial_sum(arr.begin(), arr.end(), prefix.begin() + 1);</long>
- 注意:
arr元素类型和prefix类型要匹配,避免 int 溢出,建议用long long
查询区间和时下标怎么算才不出错
闭区间 [l, r] 求和,结果是 prefix[r + 1] - prefix[l]。这个公式成立的前提是 prefix 定义为“前 i 个元素之和”(即 prefix[i] 对应 arr[0..i-1]),而不是“到下标 i 为止的和”。
一旦混淆定义,就会出现 l=0 时结果为 0,或 r=n-1 时访问越界等现象。
- 确认
l和r是有效下标:要求0 - 代入前先检查:若
l == 0,则结果应为prefix[r + 1];若r == n - 1,则需访问prefix[n]—— 所以prefix.size()必须为n + 1 - 不要写成
prefix[r] - prefix[l - 1],除非你明确定义了prefix[i]包含arr[i]
多次修改怎么办:前缀和还适用吗
纯前缀和只适合静态数组。一旦有单点更新(比如 arr[i] += delta),所有 prefix[j](j > i)都要重算,最坏 O(n) 时间 —— 这就失去预处理意义了。
此时该换数据结构:线段树或树状数组(Fenwick Tree)才是正解。它们支持 O(log n) 单点更新 + O(log n) 区间查询,代码量也不比前缀和多太多。
- 如果修改次数极少(比如 ≤ 3 次),直接重建前缀和反而更简单
- 如果修改频繁,硬用前缀和会拖慢整体性能,别被“前缀和快”的印象带偏
- 树状数组的
update和query接口比前缀和多两个函数调用,但逻辑清晰、不易出错
前缀和本身很简单,真正容易翻车的是下标偏移和边界条件 —— 多数 bug 都出在 +1 和 -1 该加在哪、该减在哪。写完务必用 l=0、r=0、l=r=n-1 这三组最简 case 手动验算一遍。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











