线段树
之前对线段树不是很明白
今天自己重新手写了一份线段树
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
struct Node {
int l, r, sum, lz;
}T[maxn << 2];
void push_up(int o) {
T[o].sum = T[o << 1].sum + T[o << 1 | 1].sum;
}
void build(int o, int L, int R) {
T[o] = Node{ L,R,0,0 };
if (L == R) {
cin >> T[o].sum;
return;
}
int mid = L + R >> 1;
build(o << 1, L, mid);
build(o << 1 | 1, mid + 1, R);
push_up(o);
}
void push_down(int o) {
if (!T[o].lz)return;
T[o << 1].lz += T[o].lz;
T[o << 1 | 1].lz += T[o].lz;
T[o << 1].sum += (T[o << 1].r - T[o << 1].l + 1) * T[o].lz;
T[o << 1 | 1].sum += (T[o << 1 | 1].r - T[o << 1 | 1].l + 1) * T[o].lz;
T[o].lz = 0;
}
void updt(int o, int l, int r, int z) {
if (l <= T[o].l && T[o].r <= r) {
T[o].lz += z;
T[o].sum += z * (T[o].r - T[o].l + 1);
return;
}
push_down(o);
int mid = T[o].l + T[o].r >> 1;
if (l <= mid) updt(o << 1, l, r, z);
if (r > mid) updt(o << 1 | 1, l, r, z);
push_up(o);
}
int query(int o, int l, int r) {
if (l <= T[o].l && T[o].r <= r) {
return T[o].sum;
}
push_down(o);
int sum = 0, mid = T[o].l + T[o].r >> 1;
if (l <= mid)sum += query(o << 1, l, r);
if (r > mid)sum += query(o << 1 | 1, l, r);
return sum;
}
要理解这份最原始的线段树代码,首先要明确的是
- (Lazy) 的含义以及作用范围
- (sum) 的含义以及作用范围
(Lazy) 作用范围是当前节点的所有子节点,不包括自己在内
(sum) 的含义是当前子树之和,包含当前节点在内
再来看 (push\_down) 和 (push\_up) 两个函数
void push_up(int o) {
T[o].sum = T[o << 1].sum + T[o << 1 | 1].sum;
}
push_up
是在回溯到根节点的时候,更新当前的 (sum)
void push_down(int o) {
if (!T[o].lz)return;
//传递标记
T[o << 1].lz += T[o].lz;
T[o << 1 | 1].lz += T[o].lz;
//作用子节点
T[o << 1].sum += (T[o << 1].r - T[o << 1].l + 1) * T[o].lz;
T[o << 1 | 1].sum += (T[o << 1 | 1].r - T[o << 1 | 1].l + 1) * T[o].lz;
T[o].lz = 0;
}
push_down
是将 (lazy) 标记往下传递,由于 (lazy) 标记的作用范围是所有子节点,所以将标记传递给子节点后,还要将标记作用在两个子节点上。
在update的时候,路径上的所有点都被push_down过,update完后,根节点的sum值就是全部的和。