线段树 (Segment Tree)
区间改 + 区间查,都 O(log n)。关键直觉:递归到"区间被完全覆盖"就停下,区间修改在那里打一个懒标记。
一、互动:一次区间操作只碰 O(log n) 个节点
这是 [1,8] 的线段树。输入区间 [x,y]:query 看哪些节点被"完全覆盖"直接返回(绿)、哪些要继续递归(蓝);update 里被完全覆盖的节点改打懒标记(紫)。没被碰到的节点是灰的。
完全覆盖 → 返回/打标记
部分重叠 → 继续递归
懒标记落点
未访问
输入区间,点 query 或 update。
二、模板核心:完全覆盖就停 + 懒标记
void update(int p, int l, int r, int x, int y, long long v) { // [x,y] += v
if (x <= l && r <= y) { sum[p] += v*(r-l+1); lazy[p] += v; return; } // 完全覆盖→打懒标记
pushdown(p, l, r); // 下行前把欠的标记传给孩子
int m = (l + r) >> 1;
if (x <= m) update(p<<1, l, m, x, y, v);
if (y > m) update(p<<1|1, m+1, r, x, y, v);
sum[p] = sum[p<<1] + sum[p<<1|1];
}
long long query(int p, int l, int r, int x, int y) {
if (x <= l && r <= y) return sum[p]; // 完全覆盖→直接返回
pushdown(p, l, r);
int m = (l + r) >> 1; long long res = 0;
if (x <= m) res += query(p<<1, l, m, x, y);
if (y > m) res += query(p<<1|1, m+1, r, x, y);
return res;
}
小结
① 结构
区间一分为二的二叉树,根管 [1,n],叶子管单点,树高 O(log n),数组开 4n。
区间一分为二的二叉树,根管 [1,n],叶子管单点,树高 O(log n),数组开 4n。
② 完全覆盖就停
递归时区间被询问完全盖住就返回/打标记 —— 这保证每层只碰常数个节点。
递归时区间被询问完全盖住就返回/打标记 —— 这保证每层只碰常数个节点。
③ 懒标记
区间改先在覆盖节点"欠着"记 lazy,下行前 pushdown 传给孩子。
区间改先在覆盖节点"欠着"记 lazy,下行前 pushdown 传给孩子。
④ 适用
区间改 + 区间查都 O(log n),比树状数组通用,但代码长、常数大。
区间改 + 区间查都 O(log n),比树状数组通用,但代码长、常数大。