Lazy标记的核心作用是避免无效递归,通过延迟更新实现O(logn)区间操作:当节点区间完全被覆盖时仅打标记并返回,访问子节点前才下推(pushDown)且清空当前标记。

Lazy标记的核心作用是避免无效递归
线段树做区间更新(比如 update(l, r, val))时,如果每次都不加区分地一路递归到叶子节点,时间复杂度会退化成 O(n)。Lazy标记本质是个「延迟承诺」:当前节点管辖的整个区间要统一加/设/异或某个值,但先不急着下推,只记在 lazy[node] 里,等真正需要访问子区间时再下传。
关键判断点:只有当当前节点的区间**完全落在查询/更新范围内**,且**还要继续往下走(比如要查子区间或更新重叠部分)**,才必须下推。否则就停在这层,打上 lazy 并返回。
常见错误现象:query(1, n) 返回正确,但 query(1, mid) 就错——说明 lazy 没在进入子节点前下推;或者更新后多次查询结果不一致——说明 lazy 下推后没清零。
- lazy 数组类型必须和操作语义匹配:区间加用
long long,区间赋值用int+ 特殊标记(如-1表示未赋值) - 下推函数
push_down(node, l, r)必须算出中点mid = (l + r) >> 1,然后更新左右子节点的tree和lazy,最后把当前lazy[node] = 0(加法)或置为无效值(赋值) - 所有访问子节点前(包括
update和query中递归调用左右子树前),都必须调用push_down
区间加法更新的 Lazy 实现要点
这是最典型的 lazy 场景,支持 add(l, r, delta)。它天然满足结合律:add(a) + add(b) == add(a+b),所以 lazy 值可直接累加。
立即学习“C++免费学习笔记(深入)”;
实操建议:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 建树时
lazy数组全初始化为0 -
push_down(node, l, r)中,若lazy[node] != 0,则:- 更新
tree[node 和 <code>tree[node :分别加上 <code>lazy[node] * (mid - l + 1)和lazy[node] * (r - mid) - 更新子节点
lazy:累加,即lazy[node - 清空当前节点
lazy[node] = 0
- 更新
- 注意:区间长度参与计算,不能漏掉乘法——这是新手最常漏的点
示例片段(关键逻辑):
void push_down(int node, int l, int r) {
if (lazy[node] == 0) return;
int mid = (l + r) >> 1;
tree[node<<1] += 1LL * lazy[node] * (mid - l + 1);
tree[node<<1|1] += 1LL * lazy[node] * (r - mid);
lazy[node<<1] += lazy[node];
lazy[node<<1|1] += lazy[node];
lazy[node] = 0;
}
区间赋值更新为什么更难处理
赋值操作(set(l, r, val))不满足叠加性:set(5) + set(3) 不等于 set(8),后一次赋值完全覆盖前一次。因此 lazy 不能简单累加,必须能表达「是否已被赋值」的状态。
典型做法是用一个特殊值(如 -1 或 INF)表示「未被赋值」,而有效值表示「该区间应全部设为此值」。
-
push_down时,若lazy[node] != -1,则左右子树的tree直接设为lazy[node] * len,lazy也设为同一值(不是累加!) -
update_set过程中,一旦当前节点区间被完全覆盖,就直接设tree[node] = val * (r-l+1),并设lazy[node] = val,然后 return - 混合操作(加法 + 赋值)需额外设计优先级,通常约定「赋值覆盖加法」,此时下推前要先清掉子节点的加法 lazy(如果存在)
别忘了 push_down 的触发时机和边界
很多人只记得在 update 里 push_down,却忘了 query 同样要——只要访问子节点,就必须确保它们的数据是最新的。
容易踩的坑:
- 在
update中,进入递归前没 push_down,导致子节点 tree 值旧、lazy 值脏 - 在
query中,只对完全包含的节点直接返回tree[node],但若该节点有 lazy,必须先 push_down 再返回?不——完全包含时你不需要子节点数据,所以可以不 push_down;但只要你下一步要进左或右子树,就必须 push_down - 递归边界写成
if (l > r)或if (l == r)错误:标准线段树递归终止条件是if (L (当前节点被完全覆盖),否则才分治 - 数组大小开太小:带 lazy 的线段树仍需
4 * n空间,少一个字节都可能越界
最隐蔽的问题:多组测试用例之间没重置 tree 和 lazy 数组,尤其 lazy 遗留脏值会导致后续所有操作错乱。

















