なんでバカのブログを読みたいの!为什么要看菜鸟的博客|

园龄:粉丝:关注:

可持久化线段树 笔记

本文原在 2024-08-19 08:38 发布于本人洛谷博客。

就是要实现每次修改都存一个版本,要求能做到查询以前版本的信息。

假设序列的长度是 \(16\),修改了 \([3,4]\),会发现只有从代表 \([3,4]\) 的节点一路到代表 \([1,16]\) 的节点会被修改。

因此,不再用 \(\times 2\)\(\times 2 + 1\) 表示左儿子和右儿子,而是用一个 \(idx\) 标号,每个节点要额外存储他的左儿子和右儿子的编号。

修改的时候,如果这个点被修改了,就新开一个点,否则直接把儿子指向上一个版本的节点。

还要开一个 \(root\) 数组存每个版本根节点的编号。

P3919 模板代码:

#include <bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
using namespace std;
const int N = 2e7 + 10;
int n, m, a[N];
namespace Segtree {
struct TREE {
int l, r, v;
} tree[N];
int idx, root[N];
int newnode(int rt) {
tree[++idx] = tree[rt];
return idx;
}
int build(int rt, int l, int r) {
rt = ++idx;
if (l == r) {
tree[rt].v = a[l];
return rt;
}
int mid = l + r >> 1;
tree[rt].l = build(tree[rt].l, l, mid);
tree[rt].r = build(tree[rt].r, mid + 1, r);
return rt;
}
int update(int rt, int l, int r, int x, int v) {
rt = newnode(rt);
if (l == r) {
tree[rt].v = v;
return rt;
}
int mid = l + r >> 1;
if (x <= mid)
tree[rt].l = update(tree[rt].l, l, mid, x, v);
else
tree[rt].r = update(tree[rt].r, mid + 1, r, x, v);
return rt;
}
int query(int rt, int l, int r, int x) {
if (l == r)
return tree[rt].v;
int mid = l + r >> 1;
if (x <= mid)
return query(tree[rt].l, l, mid, x);
else
return query(tree[rt].r, mid + 1, r, x);
}
}
using namespace Segtree;
signed main() {
IOS;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> a[i];
root[0] = build(0, 1, n);
for (int i = 1; i <= m; i++) {
int ver, opt, loc, val;
cin >> ver >> opt >> loc;
if (opt == 1) {
cin >> val;
root[i] = update(root[ver], 1, n, loc, val);
} else {
cout << query(root[ver], 1, n, loc) << "\n";
root[i] = root[ver];
}
}
return 0;
}

本文作者:Garbage fish's Blog

本文链接:https://www.cnblogs.com/Garbage-fish/p/18709936

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Garbage_fish  阅读(4)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起