程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> HDU 3436 Queue-jumpers (Splay tree)

HDU 3436 Queue-jumpers (Splay tree)

編輯:C++入門知識

三種操作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; 
    } 
    else 
        Delete();  //刪除節點 
    Insert(root,k,0);  //再插入 
    Splay(tot,0);   //旋轉至根部,這步不加會TLE 

int Get_Rank(int x){ 
    int k=Bin(x); 
    int y=node[k]; 
    Splay(y,0); 
    return size[ch[root][0]]+1; 

int Get_Kth(int r,int k){ 
    int t=size[ch[r][0]]; 
    if(k<=t) 
        return Get_Kth(ch[r][0],k); 
    else if(k<=t+num[r]) 
        return s[key[r]]+(k-t)-1; 
    else 
        return Get_Kth(ch[r][1],k-t-num[r]); 

void slove(){ 
    for(int i=0;i<q;i++){ 
        if(str[i][0]=='T') 
            Top(ope[i]); 
        else if(str[i][0]=='Q') 
            printf("%d\n",Get_Rank(ope[i])); 
        else 
            printf("%d\n",Get_Kth(root,ope[i])); 
    } 
} www.2cto.com
int main(){ 
    int t,cas=0; 
    scanf("%d",&t); 
    while(t--){ 
        scanf("%d%d",&n,&q); 
        //將所有TOP和QUERY操作的點進行離散化,將中間不變的區間縮成點 
        int total=0; 
        p[total++]=0; 
        for(int i=0;i<q;i++){ 
            scanf("%s%d",str[i],&ope[i]); 
            if(str[i][0]=='T'||str[i][0]=='Q') 
                p[total++]=ope[i]; 
        } 
        p[total++]=n; 
        sort(p,p+total); 
        cnt=0; 
        //進行離散化,s[i]表示區間起點,e[i]表示區間終點 
        for(int i=1;i<total;i++) 
            if(p[i]!=p[i-1]){ 
                if(p[i]-p[i-1]>1){   //中間的區間 
                    s[cnt]=p[i-1]+1; 
                    e[cnt]=p[i]-1; 
                    cnt++; 
                } 
                s[cnt]=p[i];   //端點 
                e[cnt]=p[i]; 
                cnt++; 
            } 
        root=tot=0;  
        ch[root][0]=ch[root][1]=pre[root]=size[root]=num[root]=key[root]=0; 
        Bulid(root,0,cnt-1,0);  //建樹 
        printf("Case %d:\n",++cas); 
        slove(); 
    } 
    return 0; 

 


作者:ACM_cxlove

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved