可持久化Treap
treap很容易可持久化,只需要每次在进行需要产生新版本的操作的split时创建新的结点即可
一般来说仅有insert和delete产生新的版本,其他操作不影响树的中序序列(但具体依题目而定)
以luoguOJ P3835为例(即每种操作均产生新版本)写可持久化treap
#include<iostream>
#include<random>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 50;
struct node{
int l, r;
int val, key;
int size;
}fhq[maxn*50];
int cnt, root[maxn];
mt19937 rnd(233); //高质量随机数生成器
inline int newnode(int val){
fhq[++cnt].val = val;
fhq[cnt].key = rnd();
fhq[cnt].size = 1;
return cnt;
}
inline void print(int x){
printf("%d\n", x);
}
inline void update(int now){
fhq[now].size = fhq[fhq[now].l].size + fhq[fhq[now].r].size + 1;
}
void split(int now, int v, int &x, int &y){ //按值分裂树,小于等于v的分裂成一棵树,大于等于v的分裂成另一棵树
if(!now) x = y = 0;
else{
if(fhq[now].val <= v){
x = ++cnt;
fhq[x] = fhq[now];
split(fhq[x].r, v, fhq[x].r, y);
update(x);
}else{
y = ++cnt;
fhq[y] = fhq[now];
split(fhq[y].l, v, x, fhq[y].l);
update(y);
}
}
}
//传入时x为较小树,y为较大树
int merge(int x, int y){
if(!x || !y) return x+y;
if(fhq[x].key > fhq[y].key){
fhq[x].r = merge(fhq[x].r, y);
update(x);
return x;
}else{
fhq[y].l = merge(x, fhq[y].l);
update(y);
return y;
}
}
int x, y, z;
inline void ins(int val, int &root){
split(root, val, x, y);
root = merge(merge(x, newnode(val)), y);
}
inline void del(int val, int &root){
split(root, val, x, z);
split(x, val-1, x, y);
y = merge(fhq[y].l, fhq[y].r);
root = merge(merge(x, y), z);
}
inline void getrank(int val, int root){
split(root, val-1, x, y);
print(fhq[x].size+1);
root = merge(x, y);
}
inline void getnum(int rank, int root){
int now = root;
while(now){
if(fhq[fhq[now].l].size + 1 == rank) break;
else if(fhq[fhq[now].l].size + 1 > rank) now = fhq[now].l;
else{
rank -= fhq[fhq[now].l].size + 1;
now = fhq[now].r;
}
}
print(fhq[now].val);
}
inline void pre(int val, int root){
split(root, val-1, x, y);
int now = x;
while(fhq[now].r) now = fhq[now].r;
print(fhq[now].val);
root = merge(x, y);
}
inline void nxt(int val, int root){
split(root, val, x, y);
int now = y;
while(fhq[now].l) now = fhq[now].l;
print(fhq[now].val);
root = merge(x, y);
}
int main(){
//node a;
int n;
scanf("%d", &n);
//newnode(531821);
for(int i = 1; i <= n; i++){
int op, x, v;
scanf("%d%d%d", &v, &op, &x);
root[i] = root[v];
switch(op){
case 1: ins(x, root[i]);
break;
case 2: del(x, root[i]);
break;
case 3: getrank(x, root[i]);
break;
case 4: getnum(x, root[i]);
break;
case 5: pre(x, root[i]);
break;
case 6: nxt(x, root[i]);
break;
}
//cout<<"**"<<cnt<<"**\n";
}
return 0;
}
通过建立根结点的副本和分裂连接处的结点副本
split时需要newnode新结点来保存新版本树的连接处(其他的部分由不同版本treap公用)