线段树 + 懒标记
可以实现 O(logn) 的区间修改了
懒标记的作用是当检测到区间会被修改时,直接修改并打上标记,在用到它的子区间时,再利用懒标记来修改儿子节点,这样,不必一直考虑到叶子节点,而是用懒标记记录需要的修改
1. 建立线段树
以下为带懒标记的线段树的基本结构
#define ls (p << 1)
#define rs (p << 1 | 1)
struct Node {
int l, r, tag, s;
}tr[N << 2];
int a[N];
void pushup(int p) {
tr[p].s = tr[ls].s + tr[rs].s;
}
void build(int p, int l, int r) {
tr[p] = {l, r, 0, 0};
if (l == r) {
tr[p].s = a[l];
return;
}
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(p);
}
// 下传懒标记,由懒标记记录的值来更新儿子节点
void pushdown(int p) {
...
}
// 区间修改
void modify(int p, int l, int r, int k) {
...
}
// 区间查询
int query(int p, int l, int r) {
...
}
2. \(pushdown\)
\(pushdown\) 的作用
将懒标记记录的修改下传至儿子节点,儿子节点就获得了修改后的值
什么时候 \(pushdown\)
一旦可能需要用到儿子节点的值时,就要 \(pushdown\) 一下,将懒标记下传
样例
void pushdown(int p) {
if (tr[p].tag) {
tr[ls].s += tr[p].tag * (tr[ls].r - tr[ls].l + 1);
tr[rs].s += tr[p].tag * (tr[rs].r - tr[rs].l + 1);
tr[ls].tag += tr[p].tag;
tr[rs].tag += tr[p].tag;
tr[p].tag = 0;
}
}
3. 区间修改
区间修改的过程中,会找到需要修改的区间,之后对区间进行修改,并打上懒标记,然后会直接返回,儿子节点没有被修改,这意味着,当我们想使用儿子节点的值时,需要先将父节点的懒标记传到该节点,即用父节点的懒标记中记录的值来更新一次儿子节点,这也是 \(pushdown\) 的作用
void modify(int p, int l, int r, int k) {
if (l <= tr[p].l && tr[p].r <= r) {
tr[p].s += (tr[p].r - tr[p].l + 1) * k;
tr[p].tag += k;
return;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(ls, l, r, k);
if (r > mid) modify(rs, l, r, k);
pushup(p);
}
4. 区间查询
在查询每个区间时,需要先将懒标记下传,然后再递归的合并这些区间
int query(int p, int l, int r) {
if (l <= tr[p].l && tr[p].r <= r) {
return tr[p].s;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
int res = 0;
if (l <= mid) res += query(ls, l, r);
if (r > mid) res += query(rs, l, r);
return res;
}
代码总览
#define ls (p << 1)
#define rs (p << 1 | 1)
const int N = 1e5 + 10;
struct Node {
int l, r, tag, s;
}tr[N << 2];
int a[N], n;
void pushup(int p) {
tr[p].s = tr[ls].s + tr[rs].s;
}
void build(int p, int l, int r) {
tr[p] = {l, r, 0, 0};
if (l == r) {
tr[p].s = a[l];
return;
}
int mid = l + r >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
pushup(p);
}
void pushdown(int p) {
if (tr[p].tag) {
tr[ls].s += (tr[ls].r - tr[ls].l + 1) * tr[p].tag;
tr[rs].s += (tr[rs].r - tr[rs].l + 1) * tr[p].tag;
tr[ls] += tr[p].tag;
tr[rs] += tr[p].tag;
tr[p].tag = 0;
}
}
void modify(int p, int l, int r, int k) {
if (l <= tr[p].l && tr[p].r <= r) {
tr[p].s += (tr[p].r - tr[p].l + 1) * k;
tr[p].tag += k;
return;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(ls, l, r, k);
if (r > mid) modify(rs, l, r, k);
pushup(p);
}
int query(int p, int l, int r) {
if (l <= tr[p].l && tr[p].r <= r) {
return tr[p].s;
}
pushdown(p);
int res = 0;
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) res += query(ls, l, r);
if (r > mid) res += query(rs, l, r);
return res;
}