(笔记)可持久化数据结构

简记

可持久化

一种允许你查询并使用历史版本的数据结构,其家族成员包括但不限于可持久化平衡树,线段树,并查集(以可持久化线段树为基础)等。

线段树

适用于一些单点修改的题上。

其基本思路是将线段树动态开点后,先建一颗完整的线段树(如果没有初值也可以不建),然后每次更新链上的节点时,新建节点并将不修改部分指向先前的线段树上的节点。

具体地,如果要修改 \(ls\)\(ls\)\(p\) 的左儿子,\(rs\)\(p\) 的右儿子,那么新建节点 \(newls\)\(newp\),令 \(newp\rightarrow rs\)\(newp\rightarrow newls\) 即可。

上述思想在例题代码中体现:

P3919 【模板】可持久化线段树 1(可持久化数组)

#include<bits/stdc++.h>
using namespace std;
const int INF=1<<30,N=1e6+5;
struct Tre{
	int v,ls,rs;
}t[N*24];
int tot,rt[N],n,m,a[N];
#define mid ((l+r)>>1)
int build(int l,int r){
	int u=++tot;
	if(l==r){t[u].v=a[l];return u;}
	t[u].ls=build(l,mid);
	t[u].rs=build(mid+1,r);
	return u;
}
int query(int u,int l,int r,int q){
	if(l==r)return t[u].v;
	if(q<=mid)return query(t[u].ls,l,mid,q);
	return query(t[u].rs,mid+1,r,q);
}
int update(int u,int l,int r,int p,int val){
	int nu=++tot;
	t[nu]=t[u];
	if(l==r){t[nu].v=val;return nu;}
	if(p<=mid)t[nu].ls=update(t[u].ls,l,mid,p,val);
	else t[nu].rs=update(t[u].rs,mid+1,r,p,val);
	return nu;
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
    	cin>>a[i];
	}
	rt[0]=build(1,n);
	for(int i=1;i<=m;i++){
		int ver,opt,loc,val;
		cin>>ver>>opt>>loc;
		if(opt==1){
			cin>>val;
			rt[i]=update(rt[ver],1,n,loc,val);
		}
		else {
			cout<<query(rt[ver],1,n,loc)<<'\n';
			rt[i]=rt[ver];
		}
	}
    return 0;
}

标记永久化

类似的把自己的儿子指针接在之前的版本上的思想,使其不能够直接在树上进行区间改操作(可能影响到之前的版本),所以就出现了标记永久化

具体来说,线段树区间改update时,仅仅将区间打上一个tag并且不进行pushdown,每次查询时加上该tag即可。

例题:P2839 [国家集训队] middle

包括不使用可持久化线段树时,该 trick 依旧适用,例如 P8543 「Wdoi-2」纯粹的复仇女神

posted @ 2025-04-24 14:46  TBSF_0207  阅读(4)  评论(0)    收藏  举报