可持久化treap主要是建立在无旋treap的基础上(好像这种东西也叫fhq treap?
通过新建节点等常规操作来达到可持久化的目的
下面先来介绍一下可持久化treap的两个常规操作:
1、split
能把一颗treap o给分成小等k和大于k的两个部分,然后把两个部分分别用x和y来存储,
观察发现这样分其实就像一刀砍下去,把treap分成两半,然后需要拼接进x和y的实际上只有这一刀会波及到的节点
然后我们递归地沿这一刀拼接x和y就好了,拼接什么的因为不能影响到之前版本,所以要用新建节点来表示(具体见下面代码)
2、merge
能把两颗treap给合并成一颗(一颗treap的所有节点需要严格小于另一颗树的所有节点),这个操作大多数时候是配合split做的,于是对于可持久化我们在这个操作上可以做一些空间优化,后面会提到
这个操作的流程比较简单,就是两个treap lo和ro一起递归,然后如果lo我们随机给他的key值大于ro的key值,那么就把lo当父亲,否则ro当父亲,然后再递归下去(具体见下面代码)
然后其他操作和普通treap一样
上一道模板题
luoguP3835
这个就是一道练习题,这里操作就不细说了,讲一下空间优化
这一题如果没有回收节点的话好像是过不去的(也有可能是我太菜了),那我们考虑一下回收
显然可以发现,每次插入节点或删除节点时会产生大量的冗余节点,这些节点对于整颗treap的形态是没有用的,就是完全是可持久化所产生的“垃圾”,可以回收掉
那么我们一个比较直观的想法是,merge时每次copy的时候直接把原来的节点给删掉
但是这样是不行的,为什么呢?我们考虑到,虽然split分开了treap然后再merge起来,这样treap的接缝处都是新建的节点,但是比如说插入节点的时候,有可能要插入的节点需要和原来版本的节点合并,这样的话就把原来版本的节点给删掉了
那么我们考虑把所有的当前版本新建节点给标记一下,当我们要copy时,如果要被copy的这个节点是当前版本新建的话,我们直接把他覆盖掉就好了
这样就可以通过这道题了
代码(未加回收):
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int INF = 2147483647;
const int maxn = 500010;
const int segn = 30 * maxn;
int n,st,ed,all,rt[maxn];
int siz[segn],cnt[segn],r[segn],v[segn],ch[segn][2],tot;
inline LL getint()
{
LL ret = 0,f = 1;
char c = getchar();
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') ret = ret * 10 + c - '0',c = getchar();
return ret * f;
}
inline void maintain(int o)
{
siz[o] = siz[ch[o][0]] + siz[ch[o][1]] + cnt[o];
}
inline void copy(int x,int o)
{
siz[x] = siz[o]; cnt[x] = cnt[o]; v[x] = v[o]; r[x] = r[o];
ch[x][0] = ch[o][0]; ch[x][1] = ch[o][1];
}
inline int newnode(int x)
{
int o = ++tot;
r[o] = rand(); v[o] = x; siz[o] = cnt[o] = 1;
}
inline void split(int o,int k,int &x,int &y)
{
if (!o) {x = y = 0; return;}
if (v[o] == k)
{
if (ch[o][0]) copy(x = ++tot,ch[o][0]); else x = 0;
if (ch[o][1]) copy(y = ++tot,ch[o][1]); else y = 0;
}
if (v[o] < k)
{
copy(x = ++tot,o);
split(ch[o][1],k,ch[x][1],y);
maintain(x);
}
if (v[o] > k)
{
copy(y = ++tot,o);
split(ch[o][0],k,x,ch[y][0]);
maintain(y);
}
}
inline int merge(int lo,int ro)
{
if (!lo || !ro) return lo | ro;
int p;
if (r[lo] < r[ro])
{
copy(p = ++tot,ro);
ch[p][0] = merge(lo,ch[ro][0]);
maintain(p);
}
else
{
copy(p = ++tot,lo);
ch[p][1] = merge(ch[lo][1],ro);
maintain(p);
}
return p;
}
inline int find(int o,int x)
{
if (!o) return 0;
if (x == v[o]) return o;
return find(ch[o][x > v[o]],x);
}
inline void insert(int o,int k,int &u)
{
int pos = find(o,k),x,y;
split(o,k,x,y);
int p;
p = newnode(k);
if (pos) siz[p] = cnt[p] = cnt[pos] + 1;
u = merge(merge(x,p),y);
}
inline void remove(int o,int k,int &u)
{
int pos = find(o,k),x,y;
if (!pos) return;
if (cnt[pos] > 1)
{
int p;
p = newnode(v[pos]);
siz[p] = cnt[p] = cnt[pos] - 1;
split(o,k,x,y);
u = merge(merge(x,pos),y);
}
else
{
split(o,k,x,y);
u = merge(x,y);
}
}
inline int pre(int o,int x)
{
if (!o) return 0;
if (v[o] < x)
{
int ret = pre(ch[o][1],x);
return !ret ? o : ret;
}
else return pre(ch[o][0],x);
}
inline int suf(int o,int x)
{
if (!o) return 0;
if (v[o] > x)
{
int ret = suf(ch[o][0],x);
return !ret ? o : ret;
}
else return suf(ch[o][1],x);
}
inline int rank(int o,int x)
{
if (!o) return 0;
if (x == v[o]) return siz[ch[o][0]];
if (x < v[o]) return rank(ch[o][0],x);
else return rank(ch[o][1],x) + siz[ch[o][0]] + cnt[o];
}
inline int kth(int o,int k)
{
if (siz[ch[o][0]] + 1 <= k && k <= siz[ch[o][0]] + cnt[o]) return o;
if (k <= siz[ch[o][0]]) return kth(ch[o][0],k);
else return kth(ch[o][1],k - siz[ch[o][0]] - cnt[o]);
}
int main()
{
n = getint();
st = newnode(-INF); ed = newnode(INF);
(r[st] > r[ed]) ? ch[st][1] = ed : ch[ed][0] = st;
rt[0] = r[st] > r[ed] ? st : ed;
for (int i = 1; i <= n; i++)
{
int u = getint(),opt = getint(),x = getint();
rt[i] = rt[u];
all += opt == 3 || opt == 4 || opt == 5 || opt == 6;
if (opt == 1) insert(rt[u],x,rt[i]);
if (opt == 2) remove(rt[u],x,rt[i]);
if (opt == 3) printf("%d\n",rank(rt[u],x));
if (opt == 4) printf("%d\n",v[kth(rt[u],x + 1)]);
if (opt == 5) printf("%d\n",v[pre(rt[u],x)]);
if (opt == 6) printf("%d\n",v[suf(rt[u],x)]);
}
return 0;
}
回收的话很简单,就不贴代码了(其实是lz懒。。)