懒惰标记(Lazy Propagation)是线段树中一种优化技术,用于**延迟更新操作**,以避免对线段树中不必要的节点进行重复计算。当线段树需要处理**区间更新**(如区间加法、区间赋值等)时,频繁地更新所有相关的叶子节点会导致效率低下。此时,懒惰标记就能通过“暂时记录更新操作,等到真正需要时才执行”的方式,显著提升性能。
---
### 懒惰标记的原理:
1. **引入懒惰数组**:我们维护一个 `lazy[]` 数组,用来记录每个节点“待执行的更新操作”。
2. **更新操作延迟执行**:
- 当对一个区间进行更新时,如果当前节点对应的区间完全包含在目标区间内,则将更新操作记录在当前节点的 `lazy` 值中,而不是立即下传到子节点。
- 在后续查询或更新操作访问子节点之前,再将 `lazy` 标记下传,执行实际的更新。
3. **标记下传函数**:在访问子节点前,必须将当前节点的 `lazy` 标记应用到子节点,并清空当前节点的 `lazy`。
---
### 示例:使用懒惰标记实现区间加法更新
下面是一个使用懒惰标记的线段树实现,支持以下操作:
- 区间加法更新(将某个区间的所有元素加上一个值)
- 区间求和查询
```cpp
#include <bits/stdc++.h>
using namespace std;
#define MAXN 100005
long long tree[MAXN * 4];
long long lazy[MAXN * 4];
void push(int node, int left, int right) {
if (lazy[node] != 0) {
// 将延迟操作下传给子节点
tree[node] += (long long)(right - left + 1) * lazy[node];
if (left != right) {
lazy[node * 2] += lazy[node];
lazy[node * 2 + 1] += lazy[node];
}
lazy[node] = 0; // 清空当前节点的懒惰标记
}
}
void build(int node, int left, int right, int *arr) {
if (left == right) {
tree[node] = arr[left];
} else {
int mid = (left + right) / 2;
build(node * 2, left, mid, arr);
build(node * 2 + 1, mid + 1, right, arr);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
}
void update_range(int node, int left, int right, int ul, int ur, long long val) {
push(node, left, right); // 先检查并处理懒惰标记
if (ur < left || ul > right) return;
if (ul <= left && right <= ur) {
lazy[node] += val;
push(node, left, right); // 再次更新当前节点
return;
}
int mid = (left + right) / 2;
update_range(node * 2, left, mid, ul, ur, val);
update_range(node * 2 + 1, mid + 1, right, ul, ur, val);
tree[node] = tree[node * 2] + tree[node * 2 + 1];
}
long long query(int node, int left, int right, int ql, int qr) {
push(node, left, right);
if (qr < left || ql > right) return 0;
if (ql <= left && right <= qr) return tree[node];
int mid = (left + right) / 2;
return query(node * 2, left, mid, ql, qr) + query(node * 2 + 1, mid + 1, right, ql, qr);
}
int main() {
int n;
cin >> n;
int *arr = new int[n + 1];
for (int i = 1; i <= n; ++i) cin >> arr[i];
build(1, 1, n, arr);
int m;
cin >> m;
while (m--) {
int op;
cin >> op;
if (op == 0) {
int l, r;
long long val;
cin >> l >> r >> val;
update_range(1, 1, n, l, r, val);
} else {
int l, r;
cin >> l >> r;
cout << query(1, 1, n, l, r) << endl;
}
}
delete[] arr;
return 0;
}
```
---
### 代码解释:
- `push()` 函数:检查当前节点是否有懒惰标记,如果有则下传到子节点,并更新当前节点的值。
- `build()`:递归构建线段树。
- `update_range()`:实现区间加法更新,结合懒惰标记减少不必要的子节点更新。
- `query()`:查询区间和,同时处理懒惰标记。
---