蒟蒻林荫小复习——Splay

首先表示对YYB大佬的崇高敬意虽然大佬根本不知道林荫是个神马东西  

在这里学的:yyb大佬的教程

好吧,我回来填坑了!

首先声明一下定义

 

struct p
{
    int v,ff,ch[2],size,cnt;
};
p t[150001];

 

t数组就是记录整颗树的数组,v代表当前点的权值,ff代表当前点的父亲,ch[0,1]分别代表左右子树(左子树上的元素小于根,右子树则大于),size代表以该节点为根的子树中元素个数,cnt代表当前点上有多少个元素(权值均为v)

pushup维护size

void pushup(int x)
{
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+t[x].cnt;
}

下面是一次旋转rotate

先想一下一次旋转会造成什么影响:假设当前节点为x,父亲为y,将x旋转到y上,x是y的k子树(k=0或1,代表左右子树)

那么:

  1. x的k^1儿子会变成y(因为(y>x)==k^1,那么假如k=0,y>x,那么将y作为x的右子树保证大于x,若k=1,y<x,将y作为x的左子树仍保证小于x)
  2. 原来x的k^1儿子被过继给y做k儿子(假定该儿子为s,若k=0,s>x且s<y[否则s会存在于y的右子树],作为y的左子树保证小于y
  3. 没了

然后注意适当的时候维护他们的ff就可以了。

void rotate(int x)
{
    int y=t[x].ff;
    int z=t[y].ff;
    int k=(t[y].ch[1]==x);
   t[z].ch[t[z].ch[1]==y]=x; t[y].ch[k]
=t[x].ch[k^1]; t[t[x].ch[k^1]].ff=y; t[x].ch[k^1]=y; t[x].ff=z; t[y].ff=x; pushup(y); pushup(x); }

下面就是Splay的核心了,Splay,这也是它快的原因

Splay的作用是将x旋转成为goal的儿子,若goal==0则将x旋转到root的位置。

现在先想一种情况:

 

 好的,图片有点大,图片引用自yyb大佬的博客。

考虑这种情况,现在我们将x旋转到root(z)的位置,然后我们查询b的位置,旋转前查询路径:Z—》Y—》X—》B

但是旋转后呢?(自己画个图)路径还是由X—》Z—》Y—》B,因为这个时候,B作为X的右儿子被过继给了Y,而B的养父Y由于被X旋转下来的Z压着,留在了社会的最底层(批斗X)这时,树的深度没有变化。

然而无论如何向上层社会进步的同时都会付出一定的代价,X向上旋转的时候B一定会被过继给Y,两次连续的旋转后就留在了社会底层,这就不平衡了对吧。那我们就顺便提高一波Y的社会地位,先将Y向上旋转,这个时候Y的左子树是X,右子树是Z,然后再将X旋转上去。

可能有的小可爱已经发现了一个问题,上面所说的问题出现在Y和X与自己的父亲大小关系相同的情况下,换言之就是X和Y同为自己父亲的左或者右子树。

因为如果Y和X不同为自己父亲的左或右子树的话,Y不会受到X和Z旋转的影响,也就是说Y和Z在X的不同子树上,不会出现Y被Z压着的情况,这个时候,树的深度就变浅了HHHHH。

 

好了,林荫开始杠精了,如果在把X翻上去之后要查询Z的儿子的话怎么办?那就再把Z的儿子翻上来不就好啦,翻着翻着树就变矮变宽了,这也就是平衡树的精髓所在

对了,这个时候,如果目标goal==0的话,root就是X啦

void Splay(int x,int goal)
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff;
        int z=t[y].ff;
        if(z!=goal)
        {
            (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        }
        rotate(x);
    }
    if(goal==0)
    {
        root=x;
    }
}

 insert就没啥好说的了,一溜烟往下找,找到了计数器++,找不到自力更生新开个点

void insert(int x)
{
    int u=root,ff;
    while(u&&t[u].v!=x)
    {
        ff=u;
        u=t[u].ch[t[u].v<x];
    }
    if(t[u].v==x)
    {
        t[u].cnt++;
    }
    else
    {
        u=++tot;
        t[u].v=x;
        t[u].ff=ff;
        if(ff)
            t[ff].ch[x>t[ff].v]=u;
        t[u].cnt=1;
        t[u].size=1;
        t[u].ch[0]=t[u].ch[1]=0;
    }
    Splay(u,0);
}

Find的操作意义是找到权值为x的节点并将其翻到树顶,也没啥可说的,一溜烟往下找,找到了就翻上去即可。

void Find(int x)
{
    int u=root;
    while(t[u].ch[x>t[u].v]&&t[u].v!=x)
    {
        u=t[u].ch[x>t[u].v];
    }
    Splay(u,0);
}

下面是前驱后缀查询操作,f=0代表查询前驱,=1代表查询后缀。

int Next(int x,int f)
{
    Find(x);
    int u=root;
    if(t[u].v>x&&f)
    {
        return u;
    }
    if(t[u].v<x&&!f)
    {
        return u;
    }
    u=t[u].ch[f];
    while(t[u].ch[f^1])
    {
        u=t[u].ch[f^1];
    }
    return u;
}

这里有一点需要注意,因为Find(x)中有Splay(u,0)语句了,实际上这个时候u点的权值就是x,这里是yyb大佬博客中的一个小坑,中间两个if是没有意义的。

下面是Kth(实际上是将序列从小到大排开第K个)这个挺简单的。

 

int Kth(int x)
{
    int u=root;
    if(t[u].size<x)
    {
        return -INF;
    }
    while(1)
    {
        int y=t[u].ch[0];
        if(t[y].size+t[u].cnt<x)
        {
            x-=t[y].size+t[u].cnt;
            u=t[u].ch[1];
        }
        else
        {
            if(t[y].size>=x)
            {
                u=t[u].ch[0];
            }
            else
                return t[u].v;
        }
    }
}

 

最后是Delete

怎样删除一个可爱的节点,那肯定是将其先变成叶子节点。查找这个节点的前驱后继,将前驱旋转到根,后继旋转到成为前驱的儿子,这个时候,目标节点就一定是后继节点的左儿子并且是叶子节点。那么就搞死它啦!

void Delete(int x)//删除x
{
    int last=Next(x,0);//查找x的前驱
    int next=Next(x,1);//查找x的后继
    splay(last,0);splay(next,last);
    //将前驱旋转到根节点,后继旋转到根节点下面
    //很明显,此时后继是前驱的右儿子,x是后继的左儿子,并且x是叶子节点
    int del=t[next].ch[0];//后继的左儿子
    if(t[del].cnt>1)//如果超过一个
    {
        t[del].cnt--;//直接减少一个
        splay(del,0);//旋转
    }
    else
        t[next].ch[0]=0;//这个节点直接丢掉(不存在了)
}//引用自yyb大佬

完结撒花!

两年后了:全装SPLAY!

 

#include<iostream>
#include<cstdio>
using namespace std;
struct PE
{
    int v,ff,ch[2],size,cnt;
};
PE t[150001];
int n,a1,a2,a3,tot=0,INF=998244353,root=0;
int ls(int x)
{
    return t[x].ch[0];
}
int rs(int x)
{
    return t[x].ch[1];
}
void pushup(int x)
{
    t[x].size=t[x].cnt+t[ls(x)].size+t[rs(x)].size;
}
void rotate(int x)
{
    int y=t[x].ff;
    int z=t[y].ff;
    int k=(t[y].ch[1]==x);
    t[z].ch[t[z].ch[1]==y]=x;
    t[y].ch[k]=t[x].ch[k^1];
    t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y;
    t[x].ff=z;
    t[y].ff=x;
    pushup(y);
    pushup(x);
}
void Splay(int x,int goal)
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff;
        int z=t[y].ff;
        if(z!=goal)
        {
            (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        }
        rotate(x);
    }
    if(goal==0)
        root=x;
}
void Insert(int x)
{
    int u=root,ff=0;
    while(u&&t[u].v!=x)
    {
        ff=u;
        u=t[u].ch[t[u].v<x];
    }
    if(t[u].v==x)
    {
        t[u].cnt++;
    }
    else
    {
        u=++tot;
        t[u].v=x;
        t[u].ff=ff;
        if(ff)
        {
            t[ff].ch[t[ff].v<x]=u;
        }
        t[u].size=1;
        t[u].cnt=1;
        t[u].ch[0]=t[u].ch[1]=0;
    }
    Splay(u,0);
} 
void Find(int x)
{
    int u=root;
    while(t[u].ch[t[u].v<x]&&t[u].v!=x)
    {
        u=t[u].ch[t[u].v<x];
    }
    Splay(u,0);
}
int Nxt(int x,int f)
{
    Find(x);
    int u=root;
    if(t[u].v>x&&f)
        return u;
    if(t[u].v<x&&!f)
        return u;
    u=t[u].ch[f];
    while(t[u].ch[f^1])
    {
        u=t[u].ch[f^1];
    }
    return u;
}
int Kth(int x)
{
    int u=root;
    if(t[u].size<x)
    {
        return -INF;
    }
    while(1)
    {
        int y=t[u].ch[0];
        if(t[y].size+t[u].cnt<x)
        {
            x=x-t[y].size;
            x=x-t[u].cnt;
            u=t[u].ch[1];
        }
        else
        {
            if(t[y].size>=x)
                u=t[u].ch[0];
            else
                return t[u].v;
        }
    }
}
int Rank(int x)
{
    Find(x);
    int u=root;
    if(t[u].v==x)
    {
        return t[ls(u)].size+1;
    }
    if(t[u].v<x)
    {
        return t[ls(u)].size+t[u].cnt+1;
    }
    else
        return t[ls(u)].size+1;
}
void Delete(int x)
{
    int last=Nxt(x,0);
    int nxt=Nxt(x,1);
    Splay(last,0);
    Splay(nxt,last);
    int del=t[nxt].ch[0];
    if(t[del].cnt>1)
    {
        t[del].cnt--;
        Splay(del,0);
    }
    else
    {
        t[nxt].ch[0]=0;
    }
}
int main()
{
    Insert(INF);
    Insert(-INF);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>a1>>a2;
        if(a1==1)
        {
            Insert(a2);
        } 
        if(a1==2)
        {
            Delete(a2);
        }
        if(a1==3)
        {
            cout<<Rank(a2)-1<<endl;
        }
        if(a1==4)
        {
            a2++;
            cout<<Kth(a2)<<endl;
        }
        if(a1==5)
        {
            cout<<t[Nxt(a2,0)].v<<endl;
        }
        if(a1==6)
        {
            cout<<t[Nxt(a2,1)].v<<endl;
        }
    }
    return 0;
}

 

posted @ 2019-08-16 11:13  HA-SY林荫  阅读(201)  评论(0编辑  收藏  举报