zoukankan      html  css  js  c++  java
  • 第二棵树:Splay

           Splay这东西神难打……什么都没动板子敲上就直逼200行了,而且非常难记(仿佛是模板长的必然结果)。但是为什么还要学呢?据说是因为它可以实现区间操作。但是自从我得知无旋Treap也能做到这些,默默对比了一下代码长度之后分分钟抛弃Splay啊= =。

          和Treap用随机值和左右旋维护平衡不同的,Splay用它的核心操作Splay来维护平衡。所谓的Splay操作可以把任何一个节点旋转到它的一个祖先节点,而旋转分单旋和双旋,双旋需要对比它与父亲是否在各自父亲的同侧。然后每次需要打标记移区间删树之类的,它居然要把目标区间的两端分别移到根和根的儿子……极其麻烦啊这个东西。放几道例题,再体会吧。见到Splay,才知Treap好。

              普通平衡树[Tyvj 1728]

                                                              时间限制:1 s   内存限制:128 MB

    【题目描述】

    您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
    1. 插入x数
    2. 删除x数(若有多个相同的数,因只删除一个)
    3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
    4. 查询排名为x的数
    5. 求x的前驱(前驱定义为小于x,且最大的数)
    6. 求x的后继(后继定义为大于x,且最小的数)

    【输入格式】

    第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

    【输出格式】

    对于操作3,4,5,6每行输出一个数,表示对应答案

    【样例输入】

    10
    1 106465
    4 1
    1 317721
    1 460929
    1 644985
    1 84185
    1 89851
    6 81968
    1 492737
    5 493598

    【样例输出】

    106465
    84185
    492737

    【提示】

    1.n的数据范围:n<=100000

    2.每个数的数据范围:[-1e7,1e7]
     
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int sj=1000010;
    int ch[sj][2],f[sj],size[sj],cnt[sj],key[sj];
    int sz,root;
    inline void clear(int x)
    {
        ch[x][0]=ch[x][1]=f[x]=size[x]=cnt[x]=key[x]=0;
    }
    inline bool get(int x)
    {
        return ch[f[x]][1]==x;
    }
    inline void update(int x)
    {
        if(x)
        {
           size[x]=cnt[x];
           if(ch[x][0]) size[x]+=size[ch[x][0]];
           if(ch[x][1]) size[x]+=size[ch[x][1]];
        }
    }
    inline void rotate(int x)
    {
        int old=f[x],oldf=f[old],whichx=get(x);
        ch[old][whichx]=ch[x][whichx^1];
        f[ch[old][whichx]]=old;
        ch[x][whichx^1]=old;
        f[old]=x;
        f[x]=oldf;
        if(oldf) ch[oldf][ch[oldf][1]==old]=x;
        update(old);
        update(x);
    }
    inline void splay(int x)
    {
        for(int fa;fa=f[x];rotate(x))
          if(f[fa]) rotate(get(x)==get(fa)?fa:x);
        root=x;
    }
    inline void insert(int x)
    {
        if(root==0) 
        {
           sz++;
           ch[sz][0]=ch[sz][1]=f[sz]=0;
           root=sz;
           size[sz]=cnt[sz]=1;
           key[sz]=x;
           return;
        }
        int now=root,fa=0;
        while(1)
        {
           if(x==key[now])
           {
              cnt[now]++;
              update(now);
              update(fa);
              splay(now);
              break;
           }
           fa=now;
           now=ch[now][key[now]<x];
           if(now==0)
           {  
              sz++;
              ch[sz][0]=ch[sz][1]=0;
              f[sz]=fa;
              size[sz]=cnt[sz]=1;
              ch[fa][key[fa]<x]=sz;
              key[sz]=x;
              update(fa);
              splay(sz);
              break;
           }
        }
    }
    inline int find(int x)
    {
         int now=root,ans=0;
         while(1)
         {
            if(x<key[now])  now=ch[now][0];
            else
            {
               ans+=(ch[now][0]?size[ch[now][0]]:0);
               if(x==key[now])
               {
                  splay(now);
                  return ans+1;
               }
               ans+=cnt[now];
               now=ch[now][1];
            }
         }
    }
    inline int findx(int x)
    {
         int now=root;
         while(1)
         {
            if(ch[now][0]&&x<=size[ch[now][0]])
              now=ch[now][0];
            else
            {
               int temp=(ch[now][0]?size[ch[now][0]]:0)+cnt[now];
               if(x<=temp) return key[now];
               x-=temp;
               now=ch[now][1];
            }
         }
    }
    inline int pre()
    {
           int now=ch[root][0];
           while(ch[now][1]) now=ch[now][1];
           return now;
    }
    inline int next()
    {
           int now=ch[root][1];
           while(ch[now][0]) now=ch[now][0];
           return now;
    }
    inline void del(int x)
    {
           int whatever=find(x);
           if(cnt[root]>1) 
           {
              cnt[root]--;
              update(root);
              return;
           }
           if(!ch[root][0]&&!ch[root][1])
           {
              clear(root);
              root=0;
              return;
           }
           if(!ch[root][0])
           {
              int oldroot=root;
              root=ch[root][1];
              f[root]=0;
              clear(oldroot);
              return;
           }
           else if(!ch[root][1])
           {
              int oldroot=root;
              root=ch[root][0];
              f[root]=0;
              clear(oldroot);
              return;
           }
           int leftbig=pre(),oldroot=root;
           splay(leftbig);
           ch[root][1]=ch[oldroot][1];
           f[ch[oldroot][1]]=root;
           clear(oldroot);
           update(root);
    }
    int main()
    {
        int n,opt,x;
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&opt,&x);
            if(opt==1) insert(x);
            if(opt==2) del(x);
            if(opt==3) printf("%d
    ",find(x));
            if(opt==4) printf("%d
    ",findx(x));
            if(opt==5) 
            {
               insert(x);
               printf("%d
    ",key[pre()]);
               del(x);
            }  
            if(opt==6)
            {
               insert(x);
               printf("%d
    ",key[next()]);
               del(x);
            }
        }
        return 0;
    }
    Splay数组实现【基本操作】

     

       [HZOI 2016][Tyvj 1729]文艺平衡树

    时间限制:1 s   内存限制:128 MB

    【题目描述】

    您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

    【输入格式】

    第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)  m表示翻转操作次数

    接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n

    【输出格式】

    输出一行n个数字,表示原始序列经过m次变换后的结果

    【样例输入】

    5 3
    1 3
    1 3
    1 4
    

    【样例输出】

    4 3 2 1 5

    【数据范围】

    N,M<=100000

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    struct node
    {
           int siz,sum,flag;
           node *ch[2],*fa;
           void pushdown(node *nd)
           {
              if(flag)
              {
                 swap(ch[0],ch[1]);
                 if(ch[0]!=nd) ch[0]->flag^=1;
                 if(ch[1]!=nd) ch[1]->flag^=1;
                 flag=0;
              }
           }
           void update()
           {
               siz=ch[0]->siz+ch[1]->siz+1;
           }
    }c[4000010],*tail=c,*root,*null;
    int n,m,a1,a2;
    void init()
    {
         null=++tail;
         null->siz=0;
         null->ch[0]=null->ch[1]=null;
         null->sum=null->flag=0;
    }
    node* newnode(node *fa)
    {
         node *nd=++tail;
         nd->fa=fa;
         nd->siz=1;
         nd->ch[1]=nd->ch[0]=null;
         nd->flag=0;
         return nd;
    }
    void rot(node*& x,int d)
    {
         node* y=x->fa;
         y->ch[!d]=x->ch[d];
         if(x->ch[d]!=null) x->ch[d]->fa=y;
         x->fa=y->fa;
         if(y->fa!=null)
            (y==y->fa->ch[0])?y->fa->ch[0]=x:y->fa->ch[1]=x;
         x->ch[d]=y;
         y->fa=x;
         x->update();
         y->update();
    }
    node *build(node *fa,int lf,int rg)
    {
         if(lf>rg) return null;
         node *nd=newnode(fa);
         if(lf==rg)
         {
            nd->sum=lf;
            return nd;
         }
         int mid=(lf+rg)>>1;
         nd->sum=mid;
         nd->ch[0]=build(nd,lf,mid-1);
         nd->ch[1]=build(nd,mid+1,rg);
         nd->update();
         return nd;
    }
    void splay(node *nd,node *tar)
    {
         while(nd->fa!=tar)
         {
            node *ne=nd->fa;
            if(nd==ne->ch[0])
            {
               if(ne->fa!=tar&&ne==ne->fa->ch[0])
                 rot(ne,1);
               rot(nd,1);
            }
            else
            {
               if(ne->fa!=tar&&ne==ne->fa->ch[1])
                 rot(ne,0);
               rot(nd,0);
            }
         }
         if(tar==null) root=nd;
    }
    node *kth(node *nd,int k)
    {
         nd->pushdown(null);
         if(nd->ch[0]->siz+1==k) return nd;
         if(nd->ch[0]->siz+1>k) return kth(nd->ch[0],k);
         else return kth(nd->ch[1],k-nd->ch[0]->siz-1);
    }
    void rev(int l,int r)
    {
         node *x=kth(root,l);
         node *y=kth(root,r+2);
         splay(x,null);
         splay(y,root);
         y->ch[0]->flag^=1;
    }
    void dfs(node *nd)
    {
         if(nd==null) return;
         nd->pushdown(null);
         dfs(nd->ch[0]);
         if(nd->sum>=1&&nd->sum<=n)
           printf("%d ",nd->sum);
         dfs(nd->ch[1]);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        init();
        root=build(null,0,n+1);
        for(int i=1;i<=m;i++)
        {
           scanf("%d%d",&a1,&a2);
           rev(a1,a2);
        }
        dfs(root);
        return 0;
    }
    Splay指针实现【区间翻转】
    SuperMemo
    Time Limit:5s    Memory Limit:65536K
    Case Time Limit:2s

    Description

    Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:

    1. ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
    2. REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
    3. REVOLVE x y T: rotate sub-sequence {Ax ... AyT times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
    4. INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
    5. DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
    6. MIN x y: query the participant what is the minimum number in sub-sequence {Ax ... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2

    To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.

    Input

    The first line contains (≤ 100000).

    The following n lines describe the sequence.

    Then follows M (≤ 100000), the numbers of operations and queries.

    The following M lines describe the operations and queries.

    Output

    For each "MIN" query, output the correct answer.

    Sample Input

    5
    1 
    2 
    3 
    4 
    5
    2
    ADD 2 4 1
    MIN 4 5

    Sample Output

    5

    Source

     
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ky ch[ch[root][1]][0]
    using namespace std;
    const int sj=200010;
    const int wq=0x3f3f3f3f;
    int n,a[sj],a1,a2,a3,m,rev[sj],mi[sj],add[sj],s[sj];
    char ss[10];
    int pre[sj],ch[sj][2],root,tot1,size[sj],key[sj],tot2;
    void newnode(int &r,int father,int k)
    {
         if(tot2) r=s[tot2--];
         else r=++tot1;
         pre[r]=father;
         ch[r][0]=ch[r][1]=0;
         key[r]=k;
         mi[r]=k;
         rev[r]=add[r]=0;
         size[r]=1;
    }
    void update_rev(int r)
    {
         if(!r) return;
         swap(ch[r][0],ch[r][1]);
         rev[r]^=1; 
    }
    void update_add(int r,int d)
    {
         if(!r) return;
         mi[r]+=d;
         key[r]+=d;
         add[r]+=d;
    }
    void push_up(int r)
    {
         size[r]=size[ch[r][0]]+size[ch[r][1]]+1;
         mi[r]=min(key[r],min(mi[ch[r][0]],mi[ch[r][1]]));
    }
    void push_down(int r)
    {
         if(rev[r])
         {
            update_rev(ch[r][0]);
            update_rev(ch[r][1]);
            rev[r]=0;
         }
         if(add[r])
         {
            update_add(ch[r][0],add[r]);
            update_add(ch[r][1],add[r]);
            add[r]=0;
         }
    }
    void build(int &x,int l,int r,int father)
    {
         if(l>r) return;
         int mid=(l+r)>>1;
         newnode(x,father,a[mid]);
         build(ch[x][0],l,mid-1,x);
         build(ch[x][1],mid+1,r,x);
         push_up(x);
    }
    void rotate(int x,int kind)
    {
         int y=pre[x];
         push_down(y);
         push_down(x);
         ch[y][!kind]=ch[x][kind];
         pre[ch[x][kind]]=y;
         if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x;
         pre[x]=pre[y];
         ch[x][kind]=y;
         pre[y]=x;
         push_up(y);
    }
    void splay(int r,int goal)
    {
         push_down(r);
         while(pre[r]!=goal)
         {
            if(pre[pre[r]]==goal)
            {
              push_down(pre[r]);
              push_down(r);
              rotate(r,ch[pre[r]][0]==r);
            }
            else
            {
              push_down(pre[pre[r]]);
              push_down(pre[r]);
              push_down(r);
              int y=pre[r];
              int kind=ch[pre[y]][0]==y;
              if(ch[y][kind]==r)
              {
                 rotate(r,!kind);
                 rotate(r,kind);
              }
              else
              {
                 rotate(y,kind);
                 rotate(r,kind);
              }
            }
         }
         push_up(r);
         if(goal==0) root=r;
    }
    int get_kth(int r,int k)
    {
        push_down(r);
        int t=size[ch[r][0]]+1;
        if(t==k) return r;
        if(t>k)  return get_kth(ch[r][0],k);
        else     return get_kth(ch[r][1],k-t);
    }
    void Add(int x,int y,int d)
    {
        splay(get_kth(root,x),0);
        splay(get_kth(root,y+2),root);
        update_add(ky,d);
        push_up(ch[root][1]);
        push_up(root);
    }
    void reverse(int x,int y)
    {
        splay(get_kth(root,x),0);
        splay(get_kth(root,y+2),root);
        update_rev(ky);
        push_up(ch[root][1]);
        push_up(root);
    }
    void revolve(int x,int y,int t)
    {
        int len=y-x+1;
        t=(t%len+len)%len;
        splay(get_kth(root,y-t+1),0);
        splay(get_kth(root,y+2),root);
        int tmp=ky;
        ky=0;
        push_up(ch[root][0]);
        push_up(root);
        splay(get_kth(root,x),0);
        splay(get_kth(root,x+1),root);
        ky=tmp;
        pre[tmp]=ch[root][1];
        push_up(ch[root][1]);
        push_up(root);
    }
    void insert(int x,int p)
    {
        splay(get_kth(root,x+1),0);
        splay(get_kth(root,x+2),root);
        newnode(ky,ch[root][1],p);
        push_up(ch[root][1]);
        push_up(root);
    }
    void erase(int r)
    {
        if(!r) return;
        s[++tot2]=r;
        erase(ch[r][0]);
        erase(ch[r][1]);
    }
    void Delete(int x)
    {
        splay(get_kth(root,x),0);
        splay(get_kth(root,x+2),root);
        erase(ky);
        pre[ky]=0;
        ky=0;
        push_up(ch[root][1]);
        push_up(root);
    }
    int Min(int x,int y)
    {
        splay(get_kth(root,x),0);
        splay(get_kth(root,y+2),root);
        return mi[ky];
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
        mi[root]=wq;
        newnode(root,0,-1);
        newnode(ch[root][1],root,-1);
        build(ky,0,n-1,ch[root][1]);
        push_up(ch[root][1]);
        push_up(root);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
           scanf("%s%d",ss,&a1);
           if(ss[0]=='A')
               scanf("%d%d",&a2,&a3),Add(a1,a2,a3);
           if(ss[0]=='R')
           {
              scanf("%d",&a2);
              if(ss[3]=='E')  
                reverse(a1,a2);
              if(ss[3]=='O')
                scanf("%d",&a3),revolve(a1,a2,a3);
           }
           if(ss[0]=='I')
                scanf("%d",&a2),insert(a1,a2);
           if(ss[0]=='D')
                Delete(a1);
           if(ss[0]=='M')
                scanf("%d",&a2),printf("%d
    ",Min(a1,a2));
        }
        return 0;
    }
    Splay数组实现【多种区间操作】

    为天地立心,为生民请命,为往圣继绝学,为万世开太平。

  • 相关阅读:
    快速获取JOB运行结果
    快速获取DB服务器当前 MEM CPU的资源消耗
    Mongodb Sharding+Replica Set
    MongoDB replSet
    Journal工作原理
    Oracle索引梳理系列(八)- 索引扫描类型及分析(高效索引必备知识)
    Oracle索引梳理系列(七)- Oracle唯一索引、普通索引及约束的关系
    Oracle索引梳理系列(六)- Oracle索引种类之函数索引
    Oracle索引梳理系列(五)- Oracle索引种类之表簇索引(cluster index)
    Oracle索引梳理系列(四)- Oracle索引种类之位图索引
  • 原文地址:https://www.cnblogs.com/moyiii-/p/7281842.html
Copyright © 2011-2022 走看看