线段树必须用固定大小数组(≥4×n)而非vector,因vector重分配会使子节点索引失效,破坏父子位置关系,导致查询越界或返回错误最值。

线段树做RMQ比ST表慢但支持修改,构建必须用递归+数组堆式存储,否则查询会越界或结果错。
为什么不能用 vector 动态分配线段树节点
vector 在 push_back 时可能触发内存重分配,导致所有已保存的指针(比如左右子节点索引)失效;而 RMQ 查询依赖父子节点位置关系,一旦索引错位,query 返回值完全不可信。实际调试中常见 seg[2*idx] is out of bounds 或静默返回错误最大值。
- 必须用固定大小数组:容量至少为
4 * n(不是2 * n,边界 case 下会溢出) - 建树入口统一从
idx = 1开始(避免 0 作为根时左子节点为 0 导致死循环) - 若输入数组下标从 0 开始,线段树内部仍按 [l, r] 闭区间处理,别混用 0-based 和 1-based 区间逻辑
build 函数里最容易写错的三个地方
建树本身逻辑简单,但参数传错、边界判断松动、合并方式写反会导致整个树失效。尤其注意 build 的终止条件和子树合并顺序。
- 终止条件必须是
l == r,不是l >= r—— 后者在n == 0时直接跳过叶子赋值 - 递归调用前必须先算出
mid = l + (r - l) / 2,不能写成(l + r) / 2(防 int 溢出) - 合并操作必须用
seg[idx] = max(seg[2*idx], seg[2*idx+1]),不能颠倒左右子节点顺序(虽 max 交换律成立,但习惯性写错会在改造成其他运算如 gcd 时埋雷)
query 区间不匹配时怎么安全剪枝
查询时若当前节点区间与目标区间无交集,必须立即返回极小值(如 INT_MIN),而不是跳过或返回 0 —— 否则会影响上层 max() 结果。典型误判是把“无交集”写成“完全包含”的反向逻辑。
立即学习“C++免费学习笔记(深入)”;
- 交集判断写法唯一可靠的是:
if (r qr) return INT_MIN; - 完全包含时直接返回
seg[idx],别再向下递归 - 部分重叠时必须分别查左右子树再取
max,不能只查一边(例如 ql ≤ mid 就只走左子树) - 注意:
ql和qr是查询端点,要和建树时的区间语义一致(都是闭区间)
单点更新后为什么有时查不到最新值
更新函数 update 写得看似正确,但常因没回溯更新父节点、或递归终止条件漏掉叶子节点、或更新路径中途提前 return,导致上层节点值陈旧。
- 更新必须严格走到底层叶子,赋新值后再逐层向上
seg[idx] = max(seg[2*idx], seg[2*idx+1]) - 递归终止条件只能是
l == r && l == pos,不能省略l == r(否则可能停在非叶子层) - 若用非递归 update(比如 while 循环上推),要注意索引计算:父节点是
idx >> 1,且必须从叶子原始位置开始,不能从任意中间节点启程
真正卡住性能的往往不是 query 复杂度,而是 build 时多一次越界访问,或 update 后少一次父节点刷新——这些 bug 不报错,只让答案偶尔差 1。


















