线段树详细分析线段树
静态线段树
第一种模板只适合单点修改的题目,每次修改都传递到树的子节点。对于区间修改则不合适,同时,由于数组大小事先申明好,其点数也有上限。
class Solution {
public:
struct Node {
int l, r;
int v;
};
struct Node tr[100010*4];
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) return;
int mid = (l + r) >> 1;
build(u<<1, l, mid);
build(u<<1|1, mid+1, r);
}
void modify(int u, int x, int v) {
if (tr[u].l == tr[u].r) {
tr[u].v = v;
return;
}
int mid = (tr[u].l + tr[u].r) >> 1;
if (x <= mid) {
modify(u<<1, x, v);
} else {
modify(u<<1|1, x, v);
}
tr[u].v = max(tr[u<<1].v, tr[u<<1|1].v);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) {
return tr[u].v;
}
int mid = (tr[u].l + tr[u].r) >> 1;
int a = 0;
int b = 0;
if (l <= mid) {
a = query(u<<1, l, r);
}
if (r > mid) {
b = query(u<<1|1, l, r);
}
return max(a,b);
}
};
静态线段树+懒标记
和第一套模板相比,增加了区间修改的功能。但是点数仍然有上限,数据量顶多十万,再往上就不能适用了。
struct Node {
int l, r;
int sum;
int add; // 懒标记
} tr[400040];
void pushup(int u) {
tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void pushdown(int u) {
if (tr[u].add == 0) return;
auto &root = tr[u];
auto &left = tr[u<<1];
auto &right = tr[u<<1|1];
left.sum += (left.r - left.l + 1) * root.add;
left.add += root.add;
right.sum += (right.r - right.l + 1) * root.add;
right.add += root.add;
root.add = 0;
}
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) {
return;
}
int mid = (l + r) >> 1;
build(u<<1, l, mid);
build(u<<1|1, mid+1, r);
}
int query(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) {
return tr[u].sum;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
int v = 0;
if (l <= mid) {
v = query(u<<1, l, r);
}
if (r > mid) {
v += query(u<<1|1, l, r);
}
return v;
}
void modify(int u, int l, int r, int v) {
if (l <= tr[u].l && r >= tr[u].r) {
tr[u].sum += (tr[u].r - tr[u].l + 1) * v;
tr[u].add += v;
} else {
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if (l <= mid) {
modify(u<<1, l, r, v);
}
if (r > mid) {
modify(u<<1|1, l, r, v);
}
pushup(u);
}
}
动态开点+点数有上限+懒标记
为了解决数据量的问题,这里提供了动态开点,解除了每个树的元素都占一个固定坑位的限制。但是仍然有限制,点数不可以随便加,有上限。
struct Node {
int l;
int r;
int add;
int val;
Node() {val = add = r = l = 0;}// 这里先置零
} tr[900000]; // 申请全局变量的大小需要根据用例合理估计
int cnt = 1;
void lazyCreate(int u) {
if (tr[u].l == 0) tr[u].l = ++cnt;
if (tr[u].r == 0) tr[u].r = ++cnt;
}
void pushdown(int u, int len) {
if (tr[u].add == 0) return;
tr[tr[u].l].add += tr[u].add;
tr[tr[u].r].add += tr[u].add;
tr[tr[u].l].val += (len - len / 2) * tr[u].add;
tr[tr[u].r].val += len / 2 * tr[u].add;
tr[u].add = 0;
}
void pushup(int u) {
tr[u].val = tr[tr[u].l].val + tr[tr[u].r].val;
}
// l & r 要更新数据的范围 lc & rc 当前树节点的左右区间
void update(int u, int lc, int rc, int l, int r, int v) {
if (l <= lc && r >= rc) { // 不能用 tr[u].lc tr[u].rc 此时不一定有
tr[u].val += (rc - lc + 1) * v;
tr[u].add += v;
return;
}
// 动态开点
lazyCreate(u); // 这里设置tr[u].lc. tr[u].rc
pushdown(u, rc - lc + 1); // 采用len 的方式、
int mid = (lc + rc) >> 1;
if (l <= mid) update(tr[u].l, lc, mid, l, r, v);
if (r > mid) update(tr[u].r, mid+1, rc, l, r, v);
pushup(u);
}
int query(int u, int lc, int rc, int l, int r) {
if (l <= lc && r >= rc) {
return tr[u].val;
}
lazyCreate(u);
pushdown(u, rc - lc + 1);
int mid = (lc + rc) >> 1;
int v = 0;
if (l <= mid) v = query(tr[u].l, lc, mid, l, r);
if (r > mid) v += query(tr[u].r, mid+1, rc, l, r);
return v;
}
动态开点+点数无限制+懒标记
点数无限制,需要的时候申请,但是效率会很低。
struct Node {
struct Node* ls;
struct Node* rs;
int val; // 当前节点有多少数
int add;
Node() {
ls = nullptr;
rs = nullptr;
val = 0;
add = 0;
}
};
struct Node *root = new Node();
void pushup(struct Node* u) {
u->val = u->ls->val + u->rs->val;
}
void pushdown(struct Node* u, int len) {
if (u->ls == nullptr) {
u->ls = new Node();
}
if (u->rs == nullptr) {
u->rs = new Node();
}
if (u->add == 0) return;
u->ls->add += u->add;
u->ls->val += (len - len/2) * u->add; // 此时l r直接不能代表到底有多少个数, 因此需要len
u->rs->add += u->add;
u->rs->val += (len/2) * u->add;
u->add = 0;
}
void modify(struct Node* u, int lc, int rc, int l, int r, int v) {
if (l <= lc && r >= rc) {
u->val += (rc - lc + 1) * v;
u->add += v;
return;
}
pushdown(u, rc - lc + 1);
int mid = (lc + rc) / 2;
if (l <= mid) modify(u->ls, lc, mid, l, r, v);
if (r > mid) modify(u->rs, mid+1, rc, l, r, v);
pushup(u);
}
int query(struct Node* u, int lc, int rc, int l, int r) {
if (l <= lc && r >= rc) {
return u->val;
}
pushdown(u, rc-lc+1);
int mid = (lc + rc) / 2;
int ans = 0;
if (l <= mid) ans = query(u->ls, lc, mid, l, r);
if (r > mid) ans += query(u->rs, mid+1, rc, l, r);
return ans;
}