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.
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.
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;
}