线段树 + 懒标记

可以实现 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;
}
posted @ 2025-03-05 13:12  he_jie  阅读(74)  评论(0)    收藏  举报