splay tree旋转操作 hdu 1890

Posted chenhuan001

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了splay tree旋转操作 hdu 1890相关的知识,希望对你有一定的参考价值。

很神奇的旋转操作。

目前没看到其他数据结构能实现这个功能。平衡树不好处理区间操作,线段树很难旋转。splay tree搞这个就很简单了。

 

下面用的这个模板跑了700ms,好慢,估计是删除操作太费时了,是时候去找找其他更快的模板了。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;

#define MAXN 100100

//好慢啊 优化下
bool Add[MAXN];//延迟标记

struct Splay_Tree
{
    int cnt, rt;//cnt为节点数,rt == root

    struct Tree{
        int key;//关键字
        int num, size;//num是这个节点有多少重复,size是以这个节点为根的子树大小。
        int fa, son[2];
    }T[MAXN];

    inline void init()
    {
        cnt = 0;//初始化超级根节点(标记为0的节点)
        T[0].size = 0;
        rt = 0;
        memset(Add,0,sizeof(Add));//开始初始化0
    }
    inline void PushUp(int x)
    {
        T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
    }

    inline void PushDown(int x)
    {
        //翻转操作,这一步最为关键
        if(Add[x])
        {
            if(T[x].son[0])//
            {
                int son0 = T[x].son[0];
                //不管那么多,先旋转起来。
                swap(T[son0].son[0],T[son0].son[1]);
                Add[son0] ^= 1;
            }
            if(T[x].son[1])
            {
                int son1 = T[x].son[1];
                //不管那么多,先旋转起来。
                swap(T[son1].son[0],T[son1].son[1]);
                Add[son1] ^= 1;
            }
            Add[x]=0;//不管子节点有没有,这层一定往下推,没有子节点相当于标记无效。
        }
    }

    inline int Newnode(int key, int fa) //新建一个节点并返回
    {
        ++cnt;
        T[cnt].key=key;
        T[cnt].num=T[cnt].size=1;
        T[cnt].fa=fa;
        T[cnt].son[0]=T[cnt].son[1]=0;
        return cnt;
    }

    inline void Rotate(int x, int p) //0左旋 1右旋
    {
        int y=T[x].fa;
        PushDown(y);//你是说这个有问题。
        PushDown(x);
        T[y].son[!p]=T[x].son[p];
        T[T[x].son[p]].fa=y;
        T[x].fa=T[y].fa;
        if(T[x].fa)
            T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;
        T[x].son[p]=y;
        T[y].fa=x;
        PushUp(y);
        PushUp(x);
    }

    void Splay(int x, int To) //将x节点移动到To的子节点中
    {
        while(T[x].fa != To)
        {
            
            if(T[T[x].fa].fa == To)
            {
                //在这里面得
                PushDown(T[x].fa);
                PushDown(x);
                Rotate(x, T[T[x].fa].son[0] == x);
            }
            else
            {
                int y=T[x].fa, z=T[y].fa;
                PushDown(z);
                PushDown(y);
                PushDown(x);
                int p=(T[z].son[0] == y);
                if(T[y].son[p] == x)
                    Rotate(x, !p), Rotate(x, p); //之字旋
                else
                    Rotate(y, p), Rotate(x, p); //一字旋
            }
        }
        if(To == 0) rt=x;
    }

    int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
    {
        if(!rt || p > T[rt].size) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)
                break;
            if(p > T[T[x].son[0]].size+T[x].num)
            {
                p-=T[T[x].son[0]].size+T[x].num;
                x=T[x].son[1];
            }
            else
                x=T[x].son[0];
        }
        Splay(x, 0);
        return x;
    }

    int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
    {
        if(!rt) return 0;
        int x=rt;
        while(x)
        {
            PushDown(x);
            if(T[x].key == key) break;
            x=T[x].son[key > T[x].key];
        }
        if(x) Splay(x, 0);
        return x;
    }

    int Prev() //返回根节点的前驱 非重点
    {
        if(!rt || !T[rt].son[0]) return 0;
        int x=T[rt].son[0];
        while(T[x].son[1])
        {
            PushDown(x);
            x=T[x].son[1];
        }
        Splay(x, 0);
        return x;
    }

    int next() //返回根结点的后继 非重点
    {
        if(!rt || !T[rt].son[1]) return 0;
        int x=T[rt].son[1];
        while(T[x].son[0])
        {
            PushDown(x);
            x=T[x].son[0];
        }
        Splay(x, 0);
        return x;
    }

    void Insert(int key) //插入key值
    {
        if(!rt)
            rt=Newnode(key, 0);
        else
        {
            int x=rt, y=0;
            while(x)
            {
                PushDown(x);
                y=x;
                if(T[x].key == key)
                {
                    T[x].num++;
                    T[x].size++;
                    break;
                }
                T[x].size++;//既然一定调整
                x=T[x].son[key > T[x].key];
            }
            if(!x)
                x = T[y].son[key > T[y].key] = Newnode(key, y);
            Splay(x, 0);
        }
    }

    void Delete(int key)
    {
        //我知道什么错误了,删除一个节点前,需要先将这个点splay 到根
        int x = key;
        if(!x) return;
        Splay(x, 0);
        if(T[x].num>1)
        {
            T[x].num--;
            PushUp(x);
            return;
        }
        int y=T[x].son[0];
        PushDown(y);
        //终于找到了,只要往下找就得pushdown
        while(T[y].son[1])
        {
            y=T[y].son[1];
            PushDown(y);
        }
        int z=T[x].son[1];
        PushDown(z);
        while(T[z].son[0])
        {
            z=T[z].son[0];
            PushDown(z);
        }
        if(!y && !z)
        {
            rt=0;
            return;
        }
        if(!y)
        {
            Splay(z, 0);
            T[z].son[0]=0;
            PushUp(z);
            return;
        }
        if(!z)
        {
            Splay(y, 0);
            T[y].son[1]=0;
            PushUp(y);
            return;
        }
        Splay(y, 0);
        Splay(z, y);//前驱和后继相同是什么鬼
        T[z].son[0]=0;
        PushUp(z);
        PushUp(y);
    }

//    int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
//    {
//        //我没有写PUSH_UP 和 PUSH_DOWN
//        if(!rt) return 0;
//        int x=rt, ret=0, y=0;
//        while(x)
//        {
//            y=x;
//            if(T[x].key <= key)
//            {
//                ret += T[T[x].son[0]].size + T[x].num;
//                x=T[x].son[1];
//            }
//            else
//                x=T[x].son[0];
//        }
//        Splay(y, 0);
//        return ret;
//    }

//    这个删除太丑了
//    void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
//    {
//        if(!Find(l)) Insert(l);// 你这样写真的好吗? 泥煤
//        int p=Prev();
//        if(!Find(r)) Insert(r);
//        int q=next();
//        if(!p && !q)
//        {
//            rt=0;
//            return;
//        }
//        if(!p)
//        {
//            T[rt].son[0]=0;
//            PushUp(rt);
//            return;
//        }
//        if(!q)
//        {
//            Splay(p, 0);
//            T[rt].son[1]=0;
//            PushUp(rt);
//            return;
//        }
//        Splay(p, q);
//        T[p].son[1]=0;
//        PushUp(p);
//        PushUp(q);
//    }
    
    void display(int x)
    {
        if(x==0) return ;
        PushDown(x);
        display(T[x].son[0]);
        //printf("%d ",T[x].key);
        display(T[x].son[1]);
    }
    
}spt;

struct node
{
    int key;
    int id;
}g[MAXN];

int cmp(node t1,node t2)
{
    if(t1.key == t2.key) return t1.id<t2.id;
    return t1.key<t2.key;
}

int main() {
    //SPT 独特的旋转操作!
    int n;
    //printf("nishi sb?");
    while(scanf("%d",&n) && n)
    {
        spt.init();
        for(int i=1;i<=n;i++)
        {
            int tmp;
            scanf("%d",&tmp);
            g[i].key = tmp;
            g[i].id = i;
            spt.Insert(i);
            //只是默认了,每个数字的id 就是它在splay 中对应的下标
        }
        //死循环什么鬼。
        //printf("nishi sb ma?");
        sort(g+1,g+1+n,cmp);
        for(int i=1;i<=n;i++)//开始旋转
        {
            //step one 将当前最小的点,移动到树根处
            spt.Splay(g[i].id, 0);
            //spt.display(spt.rt);
            //printf("\n");
            //step two 将整个左子树旋转
            int sonid = spt.T[g[i].id].son[0];
            printf("%d",spt.T[sonid].size+i);
            if(i!=n) printf(" ");
            if(sonid != 0)
            {
                swap(spt.T[sonid].son[0],spt.T[sonid].son[1]);
                
                Add[sonid] ^= 1;
            }
            //这里那里GG了,果真还是这里有问题。
            //第一次就删除了两个,不能看
            //spt.display(spt.rt);
            //printf("\n");
            spt.Delete(g[i].id);
            //每次操作之后,都把结果打印一遍
            //spt.display(spt.rt);
            //printf("\n");
        }
        printf("\n");
    }
    return 0;
}

 

以上是关于splay tree旋转操作 hdu 1890的主要内容,如果未能解决你的问题,请参考以下文章

HDU 1890 Robotic Sort (Splay)

HDU 1890 Robotic Sort(splay)

HDU 1890 - Robotic Sort - [splay][区间反转+删除根节点]

HDU1890 Robotic Sort[splay 序列]

HDU 1890:Robotic Sort(Splay)

数据结构(Splay平衡树):HDU 1890 Robotic Sort