当前位置:编程学习 > C/C++ >>

HDU 3436 Queue-jumpers (Splay tree)

三种操作RANK,TOP,QUERY。尼玛一看,N的范围10^8,必定要离散化。
仔细分析3种操作:
RANK就是找出第K位是多少
TOP是将某个人移至队首,对中间区间没有影响
QUERY是某个人的位置
可以发现将QUERY和TOP操作的点单独出来,还需要把中间的其它区间缩点,只需要保存区间长度即可,便于之后的统计名次。
对于缩点后的区间,内部是有序的,而且操作不改变顺序,在统计的时候,只需要由名次和起点就能找到,对于QUERY操作,也可以把操作点也分离出来,不知道会不会TLE
离散化之后便是Splay的基本操作了。
TOP:将目标点旋转至根部,然后删除,最后插入到队首
RANK:通过size查找即可,注意每个点的size是区间长度
QUERY:把该点旋转至根部,左子树的大小+1便是结果
Splay的旋转太神奇了,Orz
[cpp]
#include<iostream> 
#include<cstring> 
#include<cstdio> 
#include<algorithm> 
#define N 100015 
#define inf 1<<29 
#define LL long long 
#define Key_value ch[ch[root][1]][0] 
using namespace std; 
int n,q,p[N],cnt,s[2*N],e[2*N],ope[N]; 
int node[2*N]; 
char str[N][10]; 
int root,tot,size[2*N],key[2*N],pre[2*N],ch[2*N][2],num[2*N]; 
//debug部分COPY自HH 
void Treaval(int x) {   
    if(x) {   
        Treaval(ch[x][0]);   
        printf("结点%2d:左儿子 %2d 右儿子 %2d 父结点 %2d size = %2d ,key = %2d   num= %2d \n",x,ch[x][0],ch[x][1],pre[x],size[x],key[x],num[x]);   
        Treaval(ch[x][1]);   
    }   
}   
void debug() {printf("%d\n",root);Treaval(root);}    
void Push_Up(int r){ 
    size[r]=size[ch[r][0]]+size[ch[r][1]]+num[r]; 

void NewNode(int &r,int father,int k){ 
    r=++tot; 
    pre[r]=father; 
    size[r]=e[k]-s[k]+1; 
    num[r]=e[k]-s[k]+1; 
    key[r]=k; 
    node[k]=r; 
    ch[r][0]=ch[r][1]=0; 

void Bulid(int &x,int l,int r,int father){ 
    if(l>r) 
        return; 
    int mid=(l+r)/2; 
    NewNode(x,father,mid); 
    Bulid(ch[x][0],l,mid-1,x); 
    Bulid(ch[x][1],mid+1,r,x); 
    Push_Up(x); 

void Rotate(int x,int kind){   
    int y=pre[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){   
    while(pre[r]!=goal){   
        if(pre[pre[r]]==goal)   
            Rotate(r,ch[pre[r]][0]==r);   
        else{   
            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 Bin(int x){   //离散化中,二分查找 
    int low=0,high=cnt-1,mid; 
    while(low<=high){ 
        mid=(low+high)>>1; 
        if(s[mid]<=x&&e[mid]>=x) 
            return mid; 
        if(e[mid]<x) 
            low=mid+1; 
        else 
            high=mid-1; 
    } 

int Get_Min(int r){ 
    Push_Up(r); 
    while(ch[r][0]){ 
        r=ch[r][0]; 
        Push_Up(r); 
    } 
    return r; 

void Delete(){ 
    int k=Get_Min(ch[root][1]);  //找到右孩子中最小的 
    Splay(k,root);   //旋转过来,使得右子树没有左孩子 
    ch[ch[root][1]][0]=ch[root][0];   //将原来的左孩子给右子树作为左孩子 
    root=ch[root][1];   //让右孩子为根 
    pre[ch[root][0]]=root;    
    pre[root]=0; 
    Push_Up(root); 

void Insert(int &r,int k,int father){ 
    if(r==0){ 
        NewNode(r,father,k); 
        return; 
    } 
    Insert(ch[r][0],k,r);  //因为是插入到队首,担心一直往左子树找 
    Push_Up(r); 

void Top(int x){ 
    int k=Bin(x); 
    int y=node[k];  //找到这个人所在区间的标号 
    Splay(y,0);   //旋转至根部 
    if(!ch[root][0]||!ch[root][1]){   //左右孩子不完整,直接将孩子拉到根部 
        root=ch[root][0]+ch[root][1]; 
        pre[root]=0; 
    } 
   

补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,