【Tyvj 1728】普通平衡樹


題面

Description

您需要寫一種數據結構(可參考題目標題),來維護一些數,其中需要提供以下操作:

  1. 插入x數
  2. 刪除x數(若有多個相同的數,因只刪除一個)
  3. 查詢x數的排名(若有多個相同的數,因輸出最小的排名)
  4. 查詢排名為x的數
  5. 求x的前驅(前驅定義為小於x,且最大的數)
  6. 求x的后繼(后繼定義為大於x,且最小的數)

Input

第一行為n,表示操作的個數,下面n行每行有兩個數opt和x,opt表示操作的序號(1<=opt<=6)

Output

對於操作3,4,5,6每行輸出一個數,表示對應答案

Sample Input

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

Sample Output

106465
84185
492737

Hint

1.n的數據范圍:n<=100000

2.每個數的數據范圍:[-1e7,1e7]

題解

啊啊啊!
splay大法好,終於學會了splay(原來敲過這個版子,但是現在會了!)
版子題
以后總結的時候再填坑

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
#define MAX 500000
inline int read()
{
    register int x=0,t=1;
    register char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-'){t=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
    return x*t;
}
int root,tot;
struct Node
{
    int ch[2];//左右兒子
    int val;//值
    int ff;//父節點
    int size;//子樹大小
    int cnt;//數字的數量
}t[MAX];
inline void pushup(int u)//下放操作
{
    t[u].size=t[t[u].ch[0]].size+t[t[u].ch[1]].size+t[u].cnt;
    //當前子樹的大小是左子樹大小加上右子樹大小當前當前節點個數 
}
inline void rotate(int x)//旋轉操作
{
    int y=t[x].ff;//y是x的父節點
    int z=t[y].ff;//z是y的父節點
    int k=(t[y].ch[1]==x);//x是y的左兒子(0)還是右兒子(1)
    t[z].ch[t[z].ch[1]==y]=x;//把x旋轉為z的兒子
    t[x].ff=z;//x的父親更新為z
    t[y].ch[k]=t[x].ch[k^1];//把x的兒子給y
    t[t[x].ch[k^1]].ff=y;//更新父節點
    t[x].ch[k^1]=y;//y變為x的
    t[y].ff=x;//y的父親更新為x
    pushup(y);pushup(x);//更新子節點數量
}
inline void splay(int x,int goal)//旋轉操作,將x旋轉為goal的兒子
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff;//x的父親節點
        int z=t[y].ff;//x的祖父節點
        if(z!=goal)//如果z不是goal
            (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
            //如果x和y同為左兒子或者右兒子先旋轉y
            //如果x和y不同為左兒子或者右兒子先旋轉x
            //如果不雙旋的話,旋轉完成之后樹的結構不會變化
        rotate(x);//再次旋轉x,將x旋轉到z的位置
    }
    if(goal==0)//如果目標位置是0,則是將x旋轉到根節點的位置
        root=x;//更新根節點
}
inline void insert(int x)//插入x
{
    int u=root,ff=0;//當前位置u,u的父節點ff
    while(u&&t[u].val!=x)//當u存在並且沒有移動到當前的值
    {
        ff=u;//向下u的兒子,父節點變為u
        u=t[u].ch[x>t[u].val];//大於當前位置則向右找,否則向左找
    }
    if(u)//存在這個值的位置
        t[u].cnt++;//增加一個數
    else//不存在這個數字,要新建一個節點來存放
    {
        u=++tot;//新節點的位置
        if(ff)//如果父節點非根
            t[ff].ch[x>t[ff].val]=u;
        t[u].ch[0]=t[u].ch[1]=0;//不存在兒子
        t[tot].ff=ff;//父節點
        t[tot].val=x;//值
        t[tot].cnt=1;//數量
        t[tot].size=1;//大小
    }
    splay(u,0);//把當前位置移到根,保證結構的平衡
}
inline void find(int x)//查找x的位置,並將其旋轉到根節點
{
    int u=root;
    if(!u)return;//樹空
    while(t[u].ch[x>t[u].val]&&x!=t[u].val)//當存在兒子並且當前位置的值不等於x
        u=t[u].ch[x>t[u].val];//跳轉到兒子,查找x的父節點
    splay(u,0);//把當前位置旋轉到根節點
}
inline int Next(int x,int f)//查找x的前驅(0)或者后繼(1)
{
    find(x);
    int u=root;//根節點,此時x的父節點(存在的話)就是根節點
    if(t[u].val>x&&f)return u;//如果當前節點的值大於x並且要查找的是后繼
    if(t[u].val<x&&!f)return u;//如果當前節點的值小於x並且要查找的是前驅
    u=t[u].ch[f];//查找后繼的話在右兒子上找,前驅在左兒子上找
    while(t[u].ch[f^1])u=t[u].ch[f^1];//要反着跳轉,否則會越來越大(越來越小)
    return u;//返回位置
}
inline 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;//這個節點直接丟掉(不存在了)
}
inline int kth(int x)//查找排名為x的數
{
    int u=root;//當前根節點
    if(t[u].size<x)//如果當前樹上沒有這么多數
        return 0;//不存在
    while(1)
    {
        int y=t[u].ch[0];//左兒子
        if(x>t[y].size+t[u].cnt)
        //如果排名比左兒子的大小和當前節點的數量要大
        {
            x-=t[y].size+t[u].cnt;//數量減少
            u=t[u].ch[1];//那么當前排名的數一定在右兒子上找
        }
        else//否則的話在當前節點或者左兒子上查找
            if(t[y].size>=x)//左兒子的節點數足夠
                u=y;//在左兒子上繼續找
            else//否則就是在當前根節點上
                return t[u].val;
    }
}
int main()
{
    int n=read();
    insert(+2147483647);
    insert(-2147483647);
    while(n--)
    {
        int opt=read();
        if(opt==1)
        {
            int x=read();
            insert(x);
        }
        if(opt==2)
        {
            int x=read();
            Delete(x);
        }
        if(opt==3)
        {
            int x=read();
            find(x);
            printf("%d\n",t[t[root].ch[0]].size);
        }
        if(opt==4)
        {
            int x=read();
            printf("%d\n",kth(x+1));
        }
        if(opt==5)
        {
            int x=read();
            printf("%d\n",t[Next(x,0)].val);
        }
        if(opt==6)
        {
            int x=read();
            printf("%d\n",t[Next(x,1)].val);
        }
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM