HDU 3436 Queue-jumpers(Splay)

Queue-jumpers

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3545    Accepted Submission(s): 962


Problem Description
Ponyo and Garfield are waiting outside the box-office for their favorite movie. Because queuing is so boring, that they want to play a game to kill the time. The game is called “Queue-jumpers”. Suppose that there are N people numbered from 1 to N stand in a line initially. Each time you should simulate one of the following operations:
1.  Top x :Take person x to the front of the queue
2.  Query x: calculate the current position of person x
3.  Rank x: calculate the current person at position x
Where x is in [1, N].
Ponyo is so clever that she plays the game very well while Garfield has no idea. Garfield is now turning to you for help.
 

Input
In the first line there is an integer T, indicates the number of test cases.(T<=50)
In each case, the first line contains two integers N(1<=N<=10^8), Q(1<=Q<=10^5). Then there are Q lines, each line contain an operation as said above.
 

Output
For each test case, output “Case d:“ at first line where d is the case number counted from one, then for each “Query x” operation ,output the current position of person x at a line, for each “Rank x” operation, output the current person at position x at a line.
 

Sample Input
  
  
3 9 5 Top 1 Rank 3 Top 7 Rank 6 Rank 8 6 2 Top 4 Top 5 7 4 Top 5 Top 2 Query 1 Rank 6
 

Sample Output
  
  
Case 1: 3 5 8 Case 2: Case 3: 3 6

【思路参考】爱神

【解题方法】

仔细分析3种操作:

RANK就是找出第K位是多少

TOP是将某个人移至队首,对中间区间没有影响

QUERY是某个人的位置

可以发现将QUERY和TOP操作的点单独出来,还需要把中间的其它区间缩点,只需要保存区间长度即可,便于之后的统计名次。

对于缩点后的区间,内部是有序的,而且操作不改变顺序,在统计的时候,只需要由名次和起点就能找到,对于QUERY操作,也可以把操作点也分离出来,不知道会不会TLE

离散化之后便是Splay的基本操作了。

TOP:将目标点旋转至根部,然后删除,最后插入到队首

RANK:通过size查找即可,注意每个点的size是区间长度

QUERY:把该点旋转至根部,左子树的大小+1便是结果

【AC 代码】

//
//HDU 3436
//Created by just_sort 2016/8/28
//All rights Reserved
//

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 200010;

int n,q,cnt;
int p[maxn],s[maxn],e[maxn],ope[maxn],node[maxn];
char str[maxn][10];
int root,tot,size[maxn],key[maxn],pre[maxn],num[maxn],ch[maxn][2];
inline void pushup(int r)
{
    size[r]=size[ch[r][0]]+size[ch[r][1]]+num[r];
}
inline 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;
}
inline void BuildTree(int &x,int l,int r,int father)
{
    if(l>r) return ;
    int mid=(l+r)/2;
    newnode(x,father,mid);
    BuildTree(ch[x][0],l,mid-1,x);
    BuildTree(ch[x][1],mid+1,r,x);
    pushup(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;
    pushup(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);
            }
        }
    }
    pushup(r);
    if(goal==0) root=r;
}

int bin(int x)
{
    int l=0,r=cnt-1,mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(s[mid]<=x&&x<=e[mid]) return mid;
        if(e[mid]<x) l=mid+1;
        else r=mid-1;
    }
    //return -1;
}

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

//void Delete()
//{
//    int k=getmin(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;
//}
//删除
//void Delete()
//{
//    int k=getmin(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;
//    pushup(root);
//}
void Delete()
{
    int m=getmin(ch[root][1]);
    Splay(m,root);
    ch[m][0]=ch[root][0];
    pre[ch[root][0]]=m;
    root=m;
    pre[root]=0;
    pushup(root);
}
//插入
void Insert(int &r,int k,int father)
{
    if(r==0){
        newnode(r,father,k);
        return ;
    }
    Insert(ch[r][0],k,r);//因为是插入到队首,所以一直往左子树找
    pushup(r);
}
//Top操作
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]);
}

int main()
{
    int cas=1,T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&q);
        int to=0;
        p[to++]=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[to++]=ope[i];
            }
        }
        p[to++]=n;
        sort(p,p+to);
        cnt=0;
        for(int i=1; i<to; 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++;
            }
        }
        //cout<<cnt<<endl;
        //system("pause");
        //
        root=tot=0;
        pre[0]=size[0]=ch[0][0]=ch[0][1]=0;
        num[0]=key[0]=0;
        BuildTree(root,0,cnt-1,0);
        //
        printf("Case %d:\n",cas++);
        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]));
            }
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值