线段树

线段树

题目:https://www.acwing.com/problem/content/1277/

/*
 题目:https://www.acwing.com/problem/content/1277/
 给定一个正整数数列 a1,a2,…,an,每一个数都在 0∼p−1 之间。
 可以对这列数进行两种操作:
 添加操作:向序列后添加一个数,序列长度变成 n+1;
 询问操作:询问这个序列中最后 L 个数中最大的数是多少。
*/
#include <bits/stdc++.h>
#define lc u << 1
#define rc u << 1 | 1
using u32 = unsigned;
using i64 = long long;
using u64 = unsigned long long;
const int N = 2e5+10;
int m;
i64 p;
int n;
int last;
struct node {
 int l, r;
 int v; // 如果是叶子节点,存储他的值;否则存储左右儿子的最大值
}tr[4*N];
void build(int u, int l, int r) {
 tr[u] = {l, r}; // 为当前节点定义左右边界,但是不加入值,因为值是在线加入的
 if (l == r) return; // 如果当前节点是叶子节点,那么我们就直接返回
 int mid = l + r >> 1; // 裂开
 build(lc, l, mid), build(rc, mid + 1, r);
}
void pushup(int u) { 
 // pushup是根据左右子节点的值传递给父节点,这道题是求左右子节点的最大值
 tr[u].v = std::max(tr[lc].v, tr[rc].v);
}
int query(int u, int l, int r) {
 if (l <= tr[u].l && tr[u].r <= r) return tr[u].v; // 如果当前区间完全包含在要查询的区间中,直接返回
 // 否则,裂开
 int mid = tr[u].l + tr[u].r >> 1;
 int v = 0; // 存储当前节点的值
 if (l <= mid) v = std::max(v, query(lc, l, r)); // 如果[l, r]与u的左子树有交集,就去找左子树中的最大值,并且更新返回值
 if (r >= mid + 1) v = std::max(v, query(rc, l, r)); // 如果[l, r]与u的右子树有交集,就去找右子树中的最大值,并且更新返回值
 return v; 
}
void modify(int u, int x, int v) {
 // 判断当前节点是不是叶子节点,如果是叶子节点,那么我们就直接更新
 // 需要注意的是,如果这个节点是叶子节点,那么它的值一定是x,因为我们一直是根据x作为线索来进行搜索的,所以搜索到的叶子节点一定是x
 // if (tr[u].l == x && tr[u].r == x) tr[u].v = v;
 if (tr[u].l == tr[u].r) tr[u].v = v;// 如果当前节点是叶子节点并且值为x,那么此节点就是待更新的节点,更新v的值
 else {
 // 否则,这个节点就只可能是非叶子节点,继续裂开
 int mid = tr[u].l + tr[u].r >> 1;
 if (x <= mid) {
 // 要修改的节点在左子树
 modify(lc, x, v);
 } else {
 // 要修改的节点在右子树
 modify(rc, x, v);
 }
 pushup(u); // 修改完成之后,再次把左右节点的较大值更新到父节点
 }
}
void solve() {
 std::cin >> m >> p;
 build(1, 1, m); // 建树
 char op[2];
 while (m --) {
 scanf("%s", op);
 if (*op == 'Q') {
 int l;
 std::cin >> l;
 last = query(1, n - l + 1, n);
 std::cout << last << "\n";
 } else {
 int x;
 std::cin >> x;
 modify(1, n + 1, ((i64)x + last) % p);
 n ++;
 }
 }
}
int main()
{
 int t = 1;
 while (t --) {
 solve();
 }
 return 0;
}

接下来对线段树的几个操作进行详解:

1、build建树操作

void build(int u, int l, int r) {
 tr[u] = {l, r}; // 为当前节点定义左右边界,但是不加入值,因为值是在线加入的
 if (l == r) return; // 如果当前节点是叶子节点,那么我们就直接返回
 int mid = l + r >> 1; // 裂开
 build(lc, l, mid), build(rc, mid + 1, r);
}

首先,我们从节点1开始,为区间的每个节点赋值。

当我们遍历到节点k,当前节点有两种情况:

1、当前节点的l == r,那么当前节点就是叶子节点,我们对其赋相应的值之后,就直接返回,不然会陷入死循环

2、否则,当前节点就是非叶子节点,由于我们要找到叶子节点才能结束,所以我们对当前节点继续分裂,对左右子节点进行递归建树操作

2、pushup操作

void pushup(int u) { 
 // pushup是根据左右子节点的值传递给父节点,这道题是求左右子节点的最大值
 tr[u].v = std::max(tr[lc].v, tr[rc].v);
}

pushup操作一般用于我们修改了u的子节点的值之后,对u进行Pushup操作,就可以在非常短的时间内对所有需要做出修改的节点的值进行修改

3、query查询操作

int query(int u, int l, int r) {
 if (l <= tr[u].l && tr[u].r <= r) return tr[u].v; // 如果当前区间完全包含在要查询的区间中,直接返回
 // 否则,裂开
 int mid = tr[u].l + tr[u].r >> 1;
 int v = 0; // 存储当前节点的值
 if (l <= mid) v = std::max(v, query(lc, l, r)); // 如果[l, r]与u的左子树有交集,就去找左子树中的最大值,并且更新返回值
 if (r >= mid + 1) v = std::max(v, query(rc, l, r)); // 如果[l, r]与u的右子树有交集,就去找右子树中的最大值,并且更新返回值
 return v; 
}

query查询操作,求出[l, r]的最大值

此处有两种情况:

1、当前节点的左右范围完全包含在需要查询的区间中,那么我们就没必要再继续往下递归,直接返回当前节点的值就行了

2、否则,当前节点的范围没有完全包含到需要查询的区间中,再次对当前节点进行分裂 =>如果[]l, r]与左子树有交集,那么我们就在左子树的[l, mid]范围内求出一个max1;如果[l, r]与右子树有交集,我们在[mid+1, r]范围内求出一个max2,于是当前节点包含在[l, r]中那部分的最大值就是max(max1, max2),然后返回

4、modify修改操作

void modify(int u, int x, int v) {
 // 判断当前节点是不是叶子节点,如果是叶子节点,那么我们就直接更新
 // 需要注意的是,如果这个节点是叶子节点,那么它的值一定是x,因为我们一直是根据x作为线索来进行搜索的,所以搜索到的叶子节点一定是x
 // if (tr[u].l == x && tr[u].r == x) tr[u].v = v;
 if (tr[u].l == tr[u].r) tr[u].v = v;// 如果当前节点是叶子节点并且值为x,那么此节点就是待更新的节点,更新v的值
 else {
 // 否则,这个节点就只可能是非叶子节点,继续裂开
 int mid = tr[u].l + tr[u].r >> 1;
 if (x <= mid) {
 // 要修改的节点在左子树
 modify(lc, x, v);
 } else {
 // 要修改的节点在右子树
 modify(rc, x, v);
 }
 pushup(u); // 修改完成之后,再次把左右节点的较大值更新到父节点
 }
}

modify:对某个值为x的叶子节点进行修改,把值改为v

首先判断当前节点是不是叶子节点?

1、当前节点是叶子节点:那么它的值就一定是x!为什么呢?因为我们是以x为线索进行搜索的,并且每次的if……else分支只能执行一个,所以最后到达的叶子节点就只能是目标节点。直接对目标节点的值进行修改

2、当前节点不是叶子节点呢?不是的话,继续分裂:并且根据mid与x的大小关系决定是修改左子树还是右子树。
由于每个节点的v存储的是儿子节点的最大值,并且我们对儿子节点进行修改了,那么我们就要更新父节点的v值。
于是pushup操作在此刻有了意义!

作者:来杯whiskey原文地址:https://www.cnblogs.com/zj-cnbolgs/p/18544936

%s 个评论

要回复文章请先登录注册