第二棵树: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\n",find(x));
        if(opt==4) printf("%d\n",findx(x));
        if(opt==5) 
        {
           insert(x);
           printf("%d\n",key[pre()]);
           del(x);
        }  
        if(opt==6)
        {
           insert(x);
           printf("%d\n",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\n",Min(a1,a2));
    }
    return 0;
}
Splay数组实现【多种区间操作】

 

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

posted @ 2017-08-03 20:56  moyiii  阅读(232)  评论(2编辑  收藏  举报