可持久化平衡树

Posted zzzc18

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可持久化平衡树相关的知识,希望对你有一定的参考价值。

Kirin:可持久化数据结构可以保留历史版本,用新建代替修改。


洛谷模板题

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作(对于各个以往的历史版本):

1.插入x数

2.删除x数(若有多个相同的数,因只删除一个,如果没有请忽略该操作)

3.查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名)

4.查询排名为x的数

5.求x的前驱(前驱定义为小于x,且最大的数,如不存在输出-2147483647)

6.求x的后继(后继定义为大于x,且最小的数,如不存在输出2147483647)

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本。(操作3, 4, 5, 6即保持原版本无变化)

每个版本的编号即为操作的序号(版本0即为初始状态,空树)


这里使用FHQ_Treap,因为这种平衡树不进行旋转,并且便于可持久化的理解

其实如果有主席树的基础,理解这个应该不难。
每次操作都要记录一个单独的根,真正有区别的的地方只在split()

int Copy_Node(int pre){
    int now=New_Node();
    tree[now]=tree[pre];
    return now;
}

PAIR split(int k,int val){
    PAIR re(0,0);
    if(!k)
        return re;
    int now=Copy_Node(k);
    if(val<tree[k].val){
        re=split(tree[k].ls,val);
        tree[now].ls=re.right;
        re.right=now;
    }
    else{
        re=split(tree[k].rs,val);
        tree[now].rs=re.left;
        re.left=now;
    }
    update(now);
    return re;
}

Copy_Node(int pre)可以新建出一个与 \(pre\) 状态相同的节点,并对这个新的节点进行修改,作为当前的版本。

这样一来,访问第 \(k\) 个版本对应的就是以 \(rt[k]\) 为根的平衡树。

时间复杂度 \(O(nlogn)\),空间复杂度同样是 \(O(nlogn)\)


#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 500000+9;
int n;
struct T{
    int ls,rs;
    int RD;
    int size,val;
}tree[22000576];
int rt[MAXN];
struct PAIR{
    int left,right;
    PAIR(int _x=0,int _y=0):left(_x),right(_y){}
};

int New_Node(int val=0){
    static int cnt;
    cnt++;
    tree[cnt].val=val;
    tree[cnt].RD=rand();
    tree[cnt].size=1;
    return cnt;
}

int Copy_Node(int pre){
    int now=New_Node();
    tree[now]=tree[pre];
    return now;
}

void update(int x){
    tree[x].size=tree[tree[x].ls].size+tree[tree[x].rs].size+1;
}

PAIR split(int k,int val){
    PAIR re(0,0);
    if(!k)
        return re;
    int now=Copy_Node(k);
    if(val<tree[k].val){
        re=split(tree[k].ls,val);
        tree[now].ls=re.right;
        re.right=now;
    }
    else{
        re=split(tree[k].rs,val);
        tree[now].rs=re.left;
        re.left=now;
    }
    update(now);
    return re;
}

int merge(int x,int y){
    if(x==0 || y==0)
        return x+y;
    if(tree[x].RD<tree[y].RD){
        tree[x].rs=merge(tree[x].rs,y);
        update(x);
        return x;
    }
    else{
        tree[y].ls=merge(x,tree[y].ls);
        update(y);
        return y;
    }
}

void insert(int pre,int now,int val){
    PAIR rt1=split(rt[pre],val);
    rt[now]=merge(rt1.left,merge(New_Node(val),rt1.right));
}

void del(int pre,int now,int val){
    PAIR rt1=split(rt[pre],val-1);
    PAIR rt2=split(rt1.right,val);
    rt2.left=merge(tree[rt2.left].ls,tree[rt2.left].rs);
    rt[now]=merge(rt1.left,merge(rt2.left,rt2.right));
}

int n2r(int pre,int now,int val){
    PAIR rt1=split(rt[pre],val-1);
    int re=tree[rt1.left].size;//由于有-INF,不再+1
    rt[now]=merge(rt1.left,rt1.right);
    return re;
}

int r2n(int now,int val){
    int k=now;
    while(val<=tree[tree[k].ls].size || val>tree[tree[k].ls].size+1){
        if(val<=tree[tree[k].ls].size)
            k=tree[k].ls;
        else{
            val-=tree[tree[k].ls].size+1;
            k=tree[k].rs;
        }
    }
    return tree[k].val;
}

int r2n(int pre,int now,int val){
    rt[now]=rt[pre];
    return r2n(rt[now],val+1);
}

int pred(int pre,int now,int val){
    PAIR rt1=split(rt[pre],val-1);
    int re=r2n(rt1.left,tree[rt1.left].size);
    rt[now]=merge(rt1.left,rt1.right);
    return re;
}

int succ(int pre,int now,int val){
    PAIR rt1=split(rt[pre],val);
    int re=r2n(rt1.right,1);
    rt[now]=merge(rt1.left,rt1.right);
    return re;
}

int main(){
    srand(time(0));
    insert(0,0,-2147483647);
    insert(0,0,2147483647);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int v,opt,x;
        scanf("%d%d%d",&v,&opt,&x);
        if(opt==1)
            insert(v,i,x);
        else if(opt==2)
            del(v,i,x);
        else if(opt==3)
            printf("%d\n",n2r(v,i,x));
        else if(opt==4)
            printf("%d\n",r2n(v,i,x));
        else if(opt==5)
            printf("%d\n",pred(v,i,x));
        else
            printf("%d\n",succ(v,i,x));
    }
    return 0;
}

以上是关于可持久化平衡树的主要内容,如果未能解决你的问题,请参考以下文章

可持久化平衡树

[luogu3835]可持久化平衡树

可持久化平衡树

P3919 模板可持久化数组(可持久化线段树/平衡树)

平衡树与可持久化treap

luogu P3919 [模板]可持久化数组(可持久化线段树/平衡树)(主席树)