欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Luogu P3919 【模板】可持久化数组 可持久化线段树/平衡树

程序员文章站 2024-03-05 15:52:01
...

可持久化数据结构我是一直想写的,今天终于找到机会了。
题目大意:维护一个长度为N的数组,支持单点修改,单点查询历史版本。

思路:
可持久化线段树:每次修改时,新建一个版本的线段树,询问时找到该版本线段树并查询。可每次新建一颗线段树代价太高,于是我们可以考虑每次只新建一部分节点。空间复杂度O(n * 2 + qlogn),时间复杂度O(nlogn + qlogn)。

具体实现:
0.关于root节点
显然每次操作,root节点都一定会访问到,并且每次都要新建。因此,建立root数组,记录是哪一个版本的root节点。

1.build建树
就像一般的线段树建树就好啦。

	inline void build(int &u, int l, int r){
		u = ++cnt;
		if(l == r){val[u] = read(); return;}
		build(ls[u], l, mid), build(rs[u], mid + 1, r);
	}

2.修改
由于题目只要求单点修改,递归边界就是l == r。每次递归,都要新建一个节点,并把它的左孩子,右孩子等参数与原节点(pre)保持一致。

	inline void New(int &u, int pre, int l, int r, int q, int v){
		u = ++cnt, ls[u] = ls[pre], rs[u] = rs[pre], val[u] = val[pre];
		if(l == r){val[u] = v; return;}
		if(q <= mid) New(ls[u], ls[pre], l, mid, q, v);
		else New(rs[u], rs[pre], mid + 1, r, q, v);
	}

3.查询
题目只要求单点查询,因此只要从该版本的root节点开始递归下去找到那个节点就行了。由于这道题要求查询也算一种操作,所以查询完毕之后直接把root设为该版本的root就行了。

	inline int query(int u, int l, int r, int q){
		if(l == r) return val[u];
		if(q <= mid) return query(ls[u], l, mid, q);
		else return query(rs[u], mid + 1, r, q);
	}

完整代码:

#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1000005;

int rt[MAXN * 20];

inline int read(){
	int k = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
	return k * f;
}

struct Chairman{
	#define mid ((l + r) >> 1)
	int ls[MAXN * 20], rs[MAXN * 20], val[MAXN * 20], cnt;
	inline void build(int &u, int l, int r){
		u = ++cnt;
		if(l == r){val[u] = read(); return;}
		build(ls[u], l, mid), build(rs[u], mid + 1, r);
	}
	inline void New(int &u, int pre, int l, int r, int q, int v){
		u = ++cnt, ls[u] = ls[pre], rs[u] = rs[pre], val[u] = val[pre];
		if(l == r){val[u] = v; return;}
		if(q <= mid) New(ls[u], ls[pre], l, mid, q, v);
		else New(rs[u], rs[pre], mid + 1, r, q, v);
	}
	inline int query(int u, int l, int r, int q){
		if(l == r) return val[u];
		if(q <= mid) return query(ls[u], l, mid, q);
		else return query(rs[u], mid + 1, r, q);
	}
	#undef mid
}T;

int main(){
	freopen("in.txt", "r", stdin);
	int n = read(), m = read();
	T.build(rt[0], 1, n);
	for(int i = 1; i <= m; i++){
		int pre = read(), opt = read(), x = read(), v;
	switch(opt){
		case 1: v = read(); T.New(rt[i], rt[pre], 1, n, x, v); break;
		case 2: printf("%d\n", T.query(rt[pre], 1, n, x)); rt[i] = rt[pre]; break;
		}
	}
	
	return 0;
}