📅  最后修改于: 2023-12-03 15:22:47.003000             🧑  作者: Mango
动态段树是一种动态建树的数据结构,它可以在不知道要建多少颗树的情况下,动态地接受节点的插入和删除操作,同时支持动态区间操作。本文将介绍如何使用动态段树进行带点更新的范围和在线查询。
给定一个长度为 $n$ 的序列 $a_i$,支持两种操作:
1.将 $a_i$ 修改为 $x$。
2.查询区间 $[l,r]$ 的和,并在其中插入一个点 $p$。
我们可以使用动态段树维护这个序列,每个节点都代表一段区间。对于每一次插入操作,我们可以先在线段树上二分找到要插入的位置,然后在叶子节点处更新值,并将值一直更新到根节点处。
对于区间查询的操作,我们可以在动态段树上进行区间查询,查询区间内所有节点的值之和并返回,同时,将要插入的点 $p$ 也一并插入到动态段树中。
const int MAXN = 1e5 + 5;
struct Node {
int lson, rson;
int sum;
} tree[MAXN * 40];
int root[MAXN], cnt;
void build(int& now, int l, int r) {
now = ++cnt;
if (l == r) {
tree[now].sum = 0;
return;
}
int mid = (l + r) >> 1;
build(tree[now].lson, l, mid);
build(tree[now].rson, mid + 1, r);
tree[now].sum = tree[tree[now].lson].sum + tree[tree[now].rson].sum;
}
void update(int& now, int pre, int l, int r, int x, int k) {
now = ++cnt;
tree[now] = tree[pre];
if (l == r) {
tree[now].sum += k;
return;
}
int mid = (l + r) >> 1;
if (x <= mid) update(tree[now].lson, tree[pre].lson, l, mid, x, k);
else update(tree[now].rson, tree[pre].rson, mid + 1, r, x, k);
tree[now].sum = tree[tree[now].lson].sum + tree[tree[now].rson].sum;
}
int query(int now, int l, int r, int L, int R) {
if (!now) return 0;
if (L <= l && r <= R) return tree[now].sum;
int mid = (l + r) >> 1, sum = 0;
if (L <= mid) sum += query(tree[now].lson, l, mid, L, R);
if (mid < R) sum += query(tree[now].rson, mid + 1, r, L, R);
return sum;
}
int main() {
int n, q;
scanf("%d%d", &n, &q);
build(root[0], 1, n);
for (int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
update(root[i], root[i - 1], 1, n, i, x);
}
while (q--) {
int opt;
scanf("%d", &opt);
if (opt == 1) {
int x, k;
scanf("%d%d", &x, &k);
update(root[x], root[x - 1], 1, n, x, k - query(root[x], 1, n, x, x));
}
else {
int l, r, p;
scanf("%d%d%d", &l, &r, &p);
printf("%d\n", query(root[r], 1, n, l, r) + p);
update(root[r + 1], root[r], 1, n, r + 1, p);
}
}
return 0;
}
build()
函数用来建立初始的线段树,update()
函数用来插入节点,query()
函数用来查询区间和。对于插入点的操作,我们需要将区间右端点的 $sum$ 值更新为 query(root[r], 1, n, l, r) + p
,否则每次查询都会把点 $p$ 计算进去,导致答案错误。