线段树用完全二叉树的数组存储,节点 p 的左右儿子为 p<<1 和 p<<1|1,开 4 倍空间。每个节点维护一个区间的信息,建树/修改/查询都在 O(logn) 的一条树链上完成。
单点修改 + 区间查询(维护区间最大值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| #define lc(p) ((p)<<1) #define rc(p) ((p)<<1|1)
const int N = 2e5 + 5; int a[N]; struct node { int l, r, val; } t[4 * N];
void push_up(int p) { t[p].val = max(t[lc(p)].val, t[rc(p)].val); }
void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if (l == r) { t[p].val = a[l]; return; } int mid = (l + r) >> 1; build(lc(p), l, mid); build(rc(p), mid + 1, r); push_up(p); }
void change(int p, int id, int x) { if (t[p].l == t[p].r) { t[p].val = x; return; } int mid = (t[p].l + t[p].r) >> 1; if (id <= mid) change(lc(p), id, x); else change(rc(p), id, x); push_up(p); }
int query(int p, int l, int r) { if (l <= t[p].l && t[p].r <= r) return t[p].val; int mid = (t[p].l + t[p].r) >> 1, ans = 0; if (l <= mid) ans = query(lc(p), l, r); if (r > mid) ans = max(ans, query(rc(p), l, r)); return ans; }
|
区间修改 + 区间查询(lazy tag,维护区间和)
区间修改借助懒标记(lazy tag):在完全包含的节点上打标记后直接返回,下次修改/查询经过其子节点时再 push_down 下传,保证 O(logn)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| #define lc(x) ((x)<<1) #define rc(x) (((x)<<1)|1)
const int N = 1e5 + 5; int a[N], n; struct node { int l, r, v, add; } t[4 * N];
void push_up(int p) { t[p].v = t[lc(p)].v + t[rc(p)].v; }
void push_down(int p) { if (t[p].add) { t[lc(p)].add += t[p].add; t[lc(p)].v += t[p].add * (t[lc(p)].r - t[lc(p)].l + 1); t[rc(p)].add += t[p].add; t[rc(p)].v += t[p].add * (t[rc(p)].r - t[rc(p)].l + 1); t[p].add = 0; } }
void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if (l == r) { t[p].v = a[l]; return; } int mid = (l + r) / 2; build(lc(p), l, mid); build(rc(p), mid + 1, r); push_up(p); }
void change(int p, int l, int r, int k) { if (l <= t[p].l && t[p].r <= r) { t[p].add += k; t[p].v += (t[p].r - t[p].l + 1) * k; return; } push_down(p); int mid = (t[p].l + t[p].r) / 2; if (l <= mid) change(lc(p), l, r, k); if (mid < r) change(rc(p), l, r, k); push_up(p); }
int query(int p, int l, int r) { if (l <= t[p].l && t[p].r <= r) return t[p].v; push_down(p); int mid = (t[p].l + t[p].r) / 2, ans = 0; if (l <= mid) ans += query(lc(p), l, r); if (mid < r) ans += query(rc(p), l, r); return ans; }
|
相关笔记:【数据结构】线段树(一) 学习笔记