非旋转Treap

作用

旋转Treap是非常经典的一种平衡树,但是由于旋转会破坏各种乱七八糟的东西,导致旋转Treap不能可持久化。而非旋转Treap根本不需要旋转,就可以维护堆性质和二叉排序树性质,从而实现可持久化(然而我太蒟蒻,依然不会可持久化,以后会了再更新)。当然,也可以和Splay一样进行区间操作(也不比Splay难写)。

实现

非旋转Treap和很多可并堆比如左偏树很像,一切操作基于Merge和Split,即合并与分裂(以下以大根堆为例)。

1.合并A和B

(合并的前提是A全小于B,否则只能启发式合并)
首先是递归边界:当A和B中有一个是null时,退出另一个。
如果A的fix(随机堆值)<B的fix,不能交换A和B,因为我们还要维护二叉排序树性质,所以就需要Merge(A,B->son[0])。这里还需要注意的是,不能Merge(B->son[0],A),因为要确保A全小于B。
如果A的fix>B的fix,同理,就Merge(A->son[1],B)。

2.将p分裂为前k个和剩下的部分

(简称左和右部分)
首先是递归边界:当p是null时,退出的两个部分为null和null。
如果k<=p->son[0]->si(子树节点个数),说明k在p的左儿子,那么就先把p->son[0]分裂为L(前k个)和R(剩下的部分),然后p->son[0]变成了R,整棵树分裂完成后左部分是L,而右部分是p。
如果k>p->son[0]->si,说明k在p的右儿子或p,由于向右走,所以要把p->son[1]分裂为L(前k-p->son[0]->si-1个)和R(剩下的部分),然后p->son[1]变成了L,整棵树分裂完成后左部分是p,右部分是R。

3.使用

分裂之后的合并就是A全小于B的合并,所以分裂与合并就可以很快速的改变Treap的结构,而不用旋转。

4.建树

非旋转Treap的建树不能像Splay一样分治建树,而要用笛卡尔树的建树方法来建树(因为笛卡尔树和Treap一模一样)。笛卡尔树的建树方法是维护最右边的一条路径,用栈储存这条路径上的节点(越深的节点越接近堆顶)。每当新来了一个节点now,就把now放到最右边,然而此时并不保证堆性质,于是把栈中所有fix小于now的fix的节点都从栈中删除,假设最后一个被删除的节点是lst,那么now的左儿子就是lst,当前栈顶(如果存在的话)的右儿子就是now。特别注意更新节点信息。

模板-普通

BZOJ3224

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define fst first
#define snd second
using namespace std;
const int maxn=100000,MAXINT=((1<<30)-1)*2+1;

int te;
//==================================================
struct Node
{
    Node *son[2];
    int si,val,fix; //val是二叉排序树权值
    void Pushup() {si=son[0]->si+1+son[1]->si;}
};
typedef Node* P_node;
typedef pair<P_node,P_node> pnn;
Node tem[maxn+5];
P_node null=tem,len=null,ro=null;
P_node newNode(int k)
{
    len++;len->son[0]=len->son[1]=null;
    len->si=1;len->fix=rand();len->val=k;
    return len;
}
P_node Merge(P_node A,P_node B) //合并
{
    if (A==null) return B;if (B==null) return A;
    if (A->fix<B->fix)
    {
        B->son[0]=Merge(A,B->son[0]);
        B->Pushup();return B;
    } else
    {
        A->son[1]=Merge(A->son[1],B);
        A->Pushup();return A;
    }
}
pnn Split(P_node p,int k) //分裂
{
    if (p==null) return pnn(null,null);
    pnn now;
    if (k<=p->son[0]->si)
    {
        now=Split(p->son[0],k);
        p->son[0]=now.snd;p->Pushup();
        now.snd=p;
    } else
    {
        now=Split(p->son[1],k-p->son[0]->si-1);
        p->son[1]=now.fst;p->Pushup();
        now.fst=p;
    }
    return now;
}
int getrank(P_node p,int k) //为避免重复,这个函数返回真正答案-1
{
    if (p==null) return 0;
    if (k<=p->val) return getrank(p->son[0],k); else
    return p->son[0]->si+1+getrank(p->son[1],k);
}
void Insert(P_node &p,int x)
{
    //插入等同于分裂出<=x的部分和>x的部分,然后插入新节点
    pnn now=Split(p,getrank(p,x));
    p=Merge(Merge(now.fst,newNode(x)),now.snd);
}
void Delete(P_node &p,int x)
{
    //删除等同于分裂出<x的部分和>=x的部分,然后删除>=x部分的第一个
    pnn now=Split(p,getrank(p,x));
    p=Merge(now.fst,Split(now.snd,1).snd);
}
int getkth(P_node p,int k)
{
    if (p==null) return MAXINT;
    if (k==p->son[0]->si+1) return p->val; else
    if (k<=p->son[0]->si) return getkth(p->son[0],k); else
    return getkth(p->son[1],k-p->son[0]->si-1);
}
int getpre(P_node p,int k)
{
    if (p==null) return MAXINT;
    if (k>p->val)
    {
        int now=p->val,nxt=getpre(p->son[1],k);
        if (nxt==MAXINT) return now; else return nxt;
    } else return getpre(p->son[0],k);
}
int getsuf(P_node p,int k)
{
    if (p==null) return MAXINT;
    if (k<p->val)
    {
        int now=p->val,nxt=getsuf(p->son[0],k);
        if (nxt==MAXINT) return now; else return nxt;
    } else return getsuf(p->son[1],k);
}
//==================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
    int tot=0,f=1;char ch=getchar(),lst=' ';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
int main()
{
    freopen("Treap.in","r",stdin);
    freopen("Treap.out","w",stdout);
    readi(te);
    while (te--)
    {
        int td,x;readi(td);readi(x);
        switch (td)
        {
            case 1:Insert(ro,x);break;
            case 2:Delete(ro,x);break;
            case 3:printf("%d\n",getrank(ro,x)+1);break;
            case 4:printf("%d\n",getkth(ro,x));break;
            case 5:printf("%d\n",getpre(ro,x));break;
            case 6:printf("%d\n",getsuf(ro,x));break;
        }
    }
    return 0;
}

模板-区间反转

BZOJ3223

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=100000,MAXINT=((1<<30)-1)*2+1;

int n,te;
//==================================================
struct Node
{
    Node *son[2];
    int si,val,fix;
    bool tag_flip; //lazy-tag
    void Pushup() {si=son[0]->si+1+son[1]->si;}
    void Addflip() {swap(son[0],son[1]);tag_flip^=1;} //加标记
};
typedef Node* P_node;
struct pnn
{
    P_node fst,snd;
    pnn(P_node a=NULL,P_node b=NULL) {fst=a;snd=b;}
};
Node tem[maxn+5];
P_node null=tem,len=null,ro=null;
P_node newNode(int k)
{
    len++;len->son[0]=len->son[1]=null;
    len->si=1;len->fix=rand();len->val=k;len->tag_flip=false;
    return len;
}
void Pushdown(P_node p) //传递标记
{
    if (p->tag_flip)
    {
        p->tag_flip=false;
        if (p->son[0]!=null) p->son[0]->Addflip();
        if (p->son[1]!=null) p->son[1]->Addflip();
    }
}
void LNR(P_node p)
{
    if (p==null) return;Pushdown(p);
    LNR(p->son[0]);printf("%d ",p->val);LNR(p->son[1]);
}
int top_b;P_node stk_b[maxn+5]; //代表最右边路径的栈
P_node Build(int L,int R)
{
    top_b=0;stk_b[1]=null;
    for (int i=L;i<=R;i++)
    {
        P_node now=newNode(i),lst=null;
        while (top_b&&stk_b[top_b]->fix<now->fix)
        {
            stk_b[top_b]->Pushup(); //更新节点信息
            lst=stk_b[top_b--]; //记录lst
        }
        if (top_b) stk_b[top_b]->son[1]=now;
        now->son[0]=lst;stk_b[++top_b]=now;
    }
    while (top_b) stk_b[top_b--]->Pushup(); //更新节点信息
    return stk_b[1]; //栈底就是根节点
}
P_node Merge(P_node A,P_node B)
{
    if (A==null) return B;if (B==null) return A;
    Pushdown(A);Pushdown(B);
    if (A->fix<B->fix)
    {
        B->son[0]=Merge(A,B->son[0]);
        B->Pushup();return B;
    } else
    {
        A->son[1]=Merge(A->son[1],B);
        A->Pushup();return A;
    }
}
pnn Split(P_node p,int k)
{
    if (p==null) return pnn(null,null);
    pnn now;Pushdown(p);
    if (k<=p->son[0]->si)
    {
        now=Split(p->son[0],k);
        p->son[0]=now.snd;p->Pushup();
        now.snd=p;
    } else
    {
        now=Split(p->son[1],k-p->son[0]->si-1);
        p->son[1]=now.fst;p->Pushup();
        now.fst=p;
    }
    return now;
}
//==================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x)
{
    int tot=0,f=1;char ch=getchar(),lst=' ';
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=getchar();}
    if (lst=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
int main()
{
    freopen("Treap.in","r",stdin);
    freopen("Treap.out","w",stdout);
    readi(n);readi(te);ro=Build(1,n);
    while (te--)
    {
        int x,y;readi(x);readi(y);y=y-x+1;
        pnn l=Split(ro,x-1),r=Split(l.snd,y);r.fst->Addflip();
        //分裂出1~x-1和x~n,再把后面分裂为x~y和y+1~n,加标记后合并
        ro=Merge(l.fst,Merge(r.fst,r.snd));
    }
    LNR(ro);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值