无旋Treap——从入门到放弃

前言

已经是从入门到放弃的第四篇了。
但是本文并不打算给大家讲无旋Treap复杂度证明一类的。
很显然每次操作都是期望 Olog(n)

什么是Treap?

Treap=Tree+heap
其核心思想在于在权值上维护一棵二叉查找树,在优先级上维护一个堆
有旋treap利用旋转操作来维护堆的性质,
而无旋treap利用有序构树维护堆的性质。

无旋Treap的两大构树顺序:权值与原排列

权值构树是很常见的一种构树方法。和有旋treap一样,左儿子与右儿子分别表示比自己小或比自己大的树,同时其优先级均低于该节点。这类问题用有旋treap也能够很好地解决。这样的题有很多,比如bzoj3224普通平衡树
但很不幸的是,很多与平衡树沾边的题目大多有维护一个原有有序序列的要求,这个要求基本上就把有旋treap干掉了。但是对于无旋treap,我们可以按原有的序列进行构树,这样就可以维护一个原有的有序排列了。

无旋treap的核心操作:split与merge

但是在构树之后肯定是有修改操作的。这点是毋庸置疑的。对于有旋treap,我们可以通过旋转来插入要加入的权值或是删除对应的节点,但对于可能需要进行区间操作的无旋treap,我们显然不能直接这样做。
此时,因为无旋treap的有序性,我们可以像一般的有旋treap一样对需要
插入/删除的部分进行定位查找。
如果是对于权值有序,像普通的有旋treap一样直接递归查找即可,
如果是对于原序列有序,则维护一组指针,也可以很方便地进行查询。
那么在查询到了相应的树中位置之后,我们需要做的就是——
把这棵树拆开

split

整个无旋treap的核心就是它的有序性。
在找到了需要操作的位置后,我们可以把这棵树拆成两棵有序的树。
对于查询到的节点,我们根据该节点左儿子的大小对这个节点应该在哪棵树进行判断,然后递归处理即可。

inline D split(Treap* pos,int k){
    if(pos==NULL) return D(NULL,NULL);
    D y;
    if(sze(pos->son[0])>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-sze(pos->son[0]));
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
merge

在你进行了加入/删除操作之后,你发现你手上现在有两到三棵树了,自然我们需要将这些树合并。合并的具体操作也是递归处理,此时就可以维护无旋treap的堆性质。
但是需要注意的是,合并时两棵树的左右位置,维护的是整棵树的有序性。

inline Treap* merge(Treap* a,Treap* b){
    if(!a) return b;
    if(!b) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}

水得如壶口瀑布一样的水题

(1)bzoj3224:普通平衡树
题面见链接 传送门
最简单最基础的权值排序外加单点修改查询

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
using namespace std;
inline int read(){
    int i=0,f=1;
    char ch;
    for(ch=getchar();!isdigit(ch);ch=getchar())
        if(ch=='-') f=-1;
    for(;isdigit(ch);ch=getchar())
        i=(i<<3)+(i<<1)+(ch^48);
    return i*f;
}
int buf[1024];
inline void write(int x){
    if(!x){putchar('0');return ;}
    if(x<0){putchar('-');x=-x;}
    while(x){buf[++buf[0]]=x%10,x/=10;}
    while(buf[0]) putchar(buf[buf[0]--]+48);
    return ;
}
struct Treap{
    Treap* son[2];
    int weight,sze,data;
    Treap(int v){
        sze=1,data=v;weight=rand();
        son[1]=son[0]=NULL;
        return ;
    }
    inline void update(){
        sze=1+(son[0]!=NULL?son[0]->sze:0)+(son[1]!=NULL?son[1]->sze:0);
        return ;
    }
}*root;
typedef pair<Treap*,Treap*>D;
inline int sze(Treap* pos){
    return pos?pos->sze:0;
}
int n,ord,x;
inline Treap* merge(Treap* a,Treap* b){
    if(!a) return b;
    if(!b) return a;
    if(a->weight<b->weight){
        a->son[1]=merge(a->son[1],b);
        a->update();
        return a;
    }else{
        b->son[0]=merge(a,b->son[0]);
        b->update();
        return b;
    }
}
inline D split(Treap* pos,int k){
    if(pos==NULL) return D(NULL,NULL);
    D y;
    if(sze(pos->son[0])>=k){
        y=split(pos->son[0],k);
        pos->son[0]=y.second;
        pos->update();
        y.second=pos;
    }else{
        y=split(pos->son[1],k-1-sze(pos->son[0]));
        pos->son[1]=y.first;
        pos->update();
        y.first=pos;
    }
    return y;
}
inline int getrank(Treap* pos,int x){
    if(pos==NULL) return 0;
    else return (pos->data>=x)?getrank(pos->son[0],x):getrank(pos->son[1],x)+1+sze(pos->son[0]);
}
inline int getkth(int k){
    D x=split(root,k-1);
    D y=split(x.second,1);
    Treap* pos=y.first;
    root=merge(x.first,merge(pos,y.second));
    return pos==NULL?0:pos->data;
}
inline void insert(int d){
    int k=getrank(root,d);
    D x=split(root,k);
    Treap* pos=new Treap(d);
    root=merge(x.first,merge(pos,x.second));
    return ;
}
inline void remove(int d){
    int k=getrank(root,d);
    D x=split(root,k);
    D y=split(x.second,1);
    root=merge(x.first,y.second);
    return ;
}
signed main(){
    n=read();
    for(int i=1;i<=n;++i){
        ord=read();x=read();
        switch(ord){
            case 1:insert(x);break;
            case 2:remove(x);break;
            case 3:write(getrank(root,x)+1);puts("");break;
            case 4:write(getkth(x));puts("");break;
            case 5:write(getkth(getrank(root,x)));puts("");break;
            case 6:write(getkth(getrank(root,
  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值