Splay与FHQ-Treap

两个一起学的,就放一块了。
主要是用来存板子。


Splay

//This is a Splay Tree.
#include <cstdio>
#include <cstring>
using namespace std;
const int N=1e5+5,INF=0x3f3f3f3f;
int n,root,cntnode;
struct node     //始终满足左小右大
{
    int fa,ch[2],val,siz,cnt;
    //int mark;   //区间反转标记
}t[N];
inline bool get(int x) {return t[t[x].fa].ch[1]==x;} //右儿子?1:0
inline void upd(int x) {t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+t[x].cnt;} //更新计数
inline void zigzag(int x)   //旋转操作
{
    int fa=t[x].fa; int gfa=t[fa].fa;
    int d1=get(x),d2=get(fa);
    t[fa].ch[d1]=t[x].ch[d1^1]; t[t[x].ch[d1^1]].fa=fa; //断开fa与x,连接fa与x的儿子
    t[gfa].ch[d2]=x; t[x].fa=gfa;   //断开gfa与fa,连接gfa与x
    t[fa].fa=x; t[x].ch[d1^1]=fa;   //连接x与fa
    upd(fa); upd(x);    //一定是先upd fa再upd x,因为此时fa是x的节点
}
void splay(int x,int goal) //伸展操作,即将一个节点x不断旋转至goal的儿子的位置
{
    while(t[x].fa!=goal)
    {
        int fa=t[x].fa; int gfa=t[fa].fa;
        int d1=get(x),d2=get(fa);
        if(gfa!=goal)
        {
            if(d1==d2) zigzag(fa);  //双旋操作——如果x与fa处在同一方向,要先旋转fa
            else zigzag(x);
        }
        zigzag(x);
    }
    if(goal==0) root=x; //这里用goal=0来实现把x变为根节点的操作
}
//以上为维持splayTree功能的基本操作,以下将实现splay的几种常见用途
void insert(int val) //插入值功能
{
    int node=root,fa=0;
    while(node && t[node].val!=val)
        fa=node,node=t[node].ch[t[node].val<val]; //通过不断遍历树来找到插入位置
    if(node) t[node].cnt++; //已有此编号
    else
    {
        node=++cntnode; if(fa) t[fa].ch[t[fa].val<val]=node;
        t[node].fa=fa; t[node].val=val; t[node].cnt=t[node].siz=1;
    }
    splay(node,0);
}
int kth(int k)  //第k小的数
{
    int node=root;
    for(;;)
    {
        int son=t[node].ch[0];
        if(k<=t[son].siz) node=son;
        else if(k>t[son].siz+t[node].cnt)
            k-=t[son].siz+t[node].cnt,
            node=t[node].ch[1];
        else return t[node].val;
    }
}
int find(int val)   //查找值
{
    int node=root;
    while(t[node].val!=val && t[node].ch[t[node].val<val])
        node=t[node].ch[t[node].val<val];
    return node;
}
int getrk(int val)
{
    splay(find(val),0);
    return t[t[root].ch[0]].siz;
}
int presuc(int val,int tp)  //查找前驱后继,tp=0为前驱,tp=1为后继
{
    splay(find(val),0); int node=root;
    if(t[node].val<val&&!tp || t[node].val>val&&tp)  //如果找到的节点满足要求就直接返回
        return node;
    node=t[node].ch[tp];
    while(t[node].ch[tp^1])
        node=t[node].ch[tp^1];  //否则找节点左/右子树中最靠右/左的节点(根据平衡树的性质
    return node;
}
void delet(int val)
{
    int pre=presuc(val,0),suc=presuc(val,1);
    splay(pre,0); splay(suc,pre);   //将pre旋转到根,suc旋转到pre的下面,那么suc的左子树就是要删除的
    if(t[t[suc].ch[0]].cnt>1)
    {
        t[t[suc].ch[0]].cnt--;  //常规的删除
        splay(t[suc].ch[0],0);
    }
    else t[suc].ch[0]=0;
}
int main()
{
    insert(-INF); insert(INF);
    scanf("%d",&n);
    for(int i=1,opt,x;i<=n;++i)
    {
        scanf("%d%d",&opt,&x);
        if(opt==1) insert(x);
        if(opt==2) delet(x);
        if(opt==3) printf("%d\n",getrk(x));
        if(opt==4) printf("%d\n",kth(x+1));
        if(opt==5) printf("%d\n",t[presuc(x,0)].val);
        if(opt==6) printf("%d\n",t[presuc(x,1)].val);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/wzzyr24/p/11965938.html