可持久化线段树 笔记

本文原在 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;
}
posted @ 2025-02-11 16:06  Garbage_fish  阅读(14)  评论(0)    收藏  举报