可持久化Treap

可持久化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<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懒。。)


猜你喜欢

转载自blog.csdn.net/joky_2002/article/details/80319910