参考博客
可持久化数据结构:可以保留每一个历史版本,若所有版本都既可以访问又可以修改,成为完全可持久化(可以回滚到某个历史版本)
时间线:
可持久化线段树
可持久化下标线段树
题目:
模板 可持久化数组
你需要维护一个长度为N的数组,支持如下几种操作:
1.在某个历史版本上修改某一个位置上的值
2.访问某个历史版本的某一位置的值
做法:
用下标线段树维护一个数组,对于每个版本建一棵线段树->空间O(QN)
用可持久化的思想进行优化
当a[id]的值发生改变时,会影响从根[1,N]–>叶子[id,id]这条链上的所有点,其他点不会被影响,受影响的点只有logN个,所有我们没必要单独再建一个线段树,可以只建新版本的logN个点,其他点与老版本共用
如图,当a[1]发生修改时的情况
这样我们既保存了旧版本又存下新版本,根节点1(白色)对应旧版本,根节点1(红色)对应新版本,这样保存下所有历史版本,而且空间得到优化
空间O(logN)
时间O(logN)
增加边复杂度为常数
赋值节点的操作
赋值节点时直接用赋值运算符 =
Node* copyNode(Node* rt){
Node *p = pool + (++top);
pool[top] = *rt;
return p;
}
Node* change(Node* rt, int id, int x){
Node *p = copyNode(rt);//修改时,复制沿途Node
if(p->l==p->r){
p->d = x;
return p;
}
int mid = (p->l + p->r)/2;
if(id <= mid) p->ls = change(p->ls, id, x);
else p->rs = change(p->rs, id, x);
return p;
}
完整代码
#include<bits/stdc++.h>
#define MAXN 1000006
using namespace std;
typedef long long ll;
int N,M;
int a[MAXN];
struct Node{
int l,r,d;
Node *ls, *rs;
} pool[30*MAXN], *rt[MAXN];
int top = 0;
Node* buildT(int l, int r){
Node* p = pool + (++top);
p->l = l; p->r = r;
if(l==r){
p->d = a[l];
return p;
}
int mid = (l + r)/2;
p->ls = buildT(l, mid);
p->rs = buildT(mid+1, r);
return p;
}
Node* copyNode(Node* rt){
Node *p = pool + (++top);
pool[top] = *rt;
return p;
}
Node* change(Node* rt, int id, int x){
Node *p = copyNode(rt);
if(p->l==p->r){
p->d = x;
return p;
}
int mid = (p->l + p->r)/2;
if(id <= mid) p->ls = change(p->ls, id, x);
else p->rs = change(p->rs, id, x);
return p;
}
int query(Node* rt, int id){
if(rt->l == rt->r){
return rt->d;
}
int mid = (rt->l + rt->r)/2;
if(id <= mid) return query(rt->ls, id);
else return query(rt->rs, id);
}
int main(){
scanf("%d%d", &N, &M);
for(int i=1;i<=N;i++){
scanf("%d", &a[i]);
}
rt[0] = buildT(1,N);
int t = 0;
int op,id,v,x;
while(M--){
scanf("%d%d", &v, &op);//在v版本就行op操作
if(op==1){
scanf("%d%d", &id, &x);
rt[++t] = change(rt[v], id, x);
}
else{
scanf("%d", &id);
rt[++t] = rt[v];
printf("%d\n", query(rt[v], id));
}
}
return 0;
}