可持久化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一样
上一道模板题
这个就是一道练习题,这里操作就不细说了,讲一下空间优化
这一题如果没有回收节点的话好像是过不去的(也有可能是我太菜了),那我们考虑一下回收
显然可以发现,每次插入节点或删除节点时会产生大量的冗余节点,这些节点对于整颗treap的形态是没有用的,就是完全是可持久化所产生的“垃圾”,可以回收掉
那么我们一个比较直观的想法是,merge时每次copy的时候直接把原来的节点给删掉
但是这样是不行的,为什么呢?我们考虑到,虽然split分开了treap然后再merge起来,这样treap的接缝处都是新建的节点,但是比如说插入节点的时候,有可能要插入的节点需要和原来版本的节点合并,这样的话就把原来版本的节点给删掉了
那么我们考虑把所有的当前版本新建节点给标记一下,当我们要copy时,如果要被copy的这个节点是当前版本新建的话,我们直接把他覆盖掉就好了
这样就可以通过这道题了
代码(未加回收):
#include<cstdio> #include<vector> #include<queue> #include<ctime> #include<algorithm> #include<cstdlib> #include<stack> #include<cstring> #include<cmath> 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懒。。)