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

题意:刚开始给出一个N,代表初始是1,2,3...N排成一行,有三种操作 Top x 将值x置于最前面 Query x 查询值x排在第几 Rank x 查询排在第x的位置是数值几

解析:这道题坑了我半天,一直超时,看了别人的博客,原来是自己 的Splay写low了,而且要加一个地方才能不超时,我代码中有注释。 数据达到10^8,显然不能建这么大一颗树,需要离散化,把Top和Query操作 的数保存下来离散化,然后处理出一段段区间 一开始按照区间段排在第几个的位置建树,要保存区间段v对应的是哪个节点的 编号,如果是Top操作,先二分找到x对应的区间k,将k对应的节点伸展到根,然后 删除这个节点,再插入最右边,如果是Query操作,也是二分找到x对应的区间k, 将k对应的节点伸展到根,即可得到他的排名。如果是Rank操作,从根开始找就行, 判断是否在某一段区间内。然后找到了就可以得到答案了。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define t tr[r]
#define y tr[fa]
const int maxn=;
int N,Q,A[maxn];//A数组用于保存离散化的数
char op[maxn][]; //操作字符串
int opx[maxn],seg[maxn][],Size;//操作值,区间左右端点,多少个区间
int BIS(int v) //二分找区间下标
{
int x=,yy=Size,mid;
while(x<=yy)
{
int mid=(x+yy)/;
if(seg[mid][]<=v&&v<=seg[mid][]) return mid; //找到了
if(seg[mid][]<v) x=mid+;
else yy=mid;
}
}
int bel[maxn]; //用于保存第几段区间现在的节点编号
struct treap
{
int son[],fa; //左右儿子和父亲
int s,v,num; //s是大小,v代表区间的下标,num代表这段区间的大小
treap(){ s=v=num=; son[]=son[]=fa=; }
int rk();//排名
void pushup(); //更新
int cmp(int k); //比较
}tr[maxn*];
int treap::rk(){ return tr[son[]].s+num; }
void treap::pushup(){ s=tr[son[]].s+tr[son[]].s+num; }
int treap::cmp(int k)
{
if(tr[son[]].s<k&&k<=rk()) return -;//在区间内
if(k<rk()) return ;
else return ;
}
struct splaytree
{
int id,root;
void init(){ id=root=; tr[].s=tr[].num=; }//初始化
void dfs(int r)
{
if(r==) return;
dfs(t.son[]);
printf("%d ",t.v);
dfs(t.son[]);
}
void Visit(){ dfs(root); puts("");}
int NewNode(int fa,int v) //得到新节点
{
bel[v]=++id; //保存v值对应的下标
int r=id;
t.fa=fa; t.v=v;
t.s=t.num=seg[v][]-seg[v][]+;
t.son[]=t.son[]=;
return r;
}
void Build(int &r,int le,int ri,int fa)//建树
{
if(le>ri) return; //区间不存在
int mid=(le+ri)/;
r=NewNode(fa,mid); //得到新节点
Build(t.son[],le,mid-,r); //左建树
Build(t.son[],mid+,ri,r); //右建树
t.pushup();
}
void Insert(int &r,int fa,int k) //插入到最左边
{
if(r==){ r=NewNode(fa,k); return; } //在最左边建立新节点
Insert(t.son[],r,k);
t.pushup();
}
int GetMin(int r) //得到这棵子树最左端的节点
{
while(t.son[]!=) r=t.son[];
return r;
}
void Rotate(int r,int d) //翻转
{
int fa=t.fa;
y.son[d^]=t.son[d];
if(t.son[d]!=) tr[t.son[d]].fa=fa;
if(y.fa==) t.fa=;
else if(tr[y.fa].son[]==fa) { t.fa=y.fa; tr[t.fa].son[]=r; }
else if(tr[y.fa].son[]==fa) { t.fa=y.fa; tr[t.fa].son[]=r; }
t.son[d]=fa;
y.fa=r;
y.pushup();
t.pushup(); }
void Splay(int r) //伸展
{
while(t.fa!=)
{
if(tr[t.fa].fa==) Rotate(r,tr[t.fa].son[]==r);
else
{
int fa=t.fa;
int d=(tr[y.fa].son[]==fa);
if(y.son[d]==r)
{
Rotate(r,d^);
Rotate(r,d);
}
else
{
Rotate(fa,d);
Rotate(r,d);
}
}
}
}
void Top(int &r,int k) //将k对应的区间置于最前面
{
r=bel[k];
Splay(r);//伸展到根
int rr=GetMin(t.son[]);//找到右子树最小的节点
Splay(rr); //伸展到根
treap& tt=tr[rr];
tt.son[]=t.son[]; //将原来的左子树连到rr上去
if(t.son[]!=) tr[t.son[]].fa=rr;
tt.fa=;
tt.pushup();
r=rr;
Insert(r,,k); //插入到最右边
Splay(r=id); //不加这个会超时。。。
}
int Query(int &r,int k)
{
r=bel[k];
Splay(r);
return t.rk();
}
int Rank(int &r,int k)
{
int d=t.cmp(k);
if(d==-) return seg[t.v][]+ k- tr[t.son[]].s - ;
if(d==) return Rank(t.son[],k);
else return Rank(t.son[],k- t.rk());
}
}spt;
int main()
{
int T,Case=;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&N,&Q);
int k=;
A[k++]=;
for(int i=;i<Q;i++)
{
scanf("%s%d",op[i],&opx[i]);
if(op[i][]=='T'||op[i][]=='Q') A[k++]=opx[i];
}
A[k++]=N+;
sort(A,A+k);
Size=;
for(int i=;i<k;i++) //处理出每段区间
{
if(A[i]==A[i-]) continue;
if(A[i]-A[i-]>){ seg[++Size][]=A[i-]+; seg[Size][]=A[i]-; }
seg[++Size][]=A[i]; seg[Size][]=A[i];
}
spt.init();
spt.Build(spt.root,,Size,);
printf("Case %d:\n",++Case);
for(int i=;i<Q;i++)
{
if(op[i][]=='T') spt.Top(spt.root,BIS(opx[i]));
else if(op[i][]=='Q') printf("%d\n",spt.Query(spt.root,BIS(opx[i])));
else printf("%d\n",spt.Rank(spt.root,opx[i]));
}
}
return ;
}

Hdu3436-Queue-jumpers(伸展树)的更多相关文章

  1. codeforces 38G - Queue splay伸展树

    题目 https://codeforces.com/problemset/problem/38/G 题意: 一些人按顺序进入队列,每个人有两个属性,地位$A$和能力$C$ 每个人进入时都在队尾,并最多 ...

  2. POJ 3580 (伸展树)

    题目链接: http://poj.org/problem?id=3580 题目大意:对一个序列进行以下六种操作.输出MIN操作的结果. 解题思路: 六个操作,完美诠释了伸展树有多么吊.注意,默认使用L ...

  3. 【BBST 之伸展树 (Splay Tree)】

    最近“hiho一下”出了平衡树专题,这周的Splay一直出现RE,应该删除操作指针没处理好,还没找出原因. 不过其他操作运行正常,尝试用它写了一道之前用set做的平衡树的题http://codefor ...

  4. 二叉查找树,AVL树,伸展树【CH4601普通平衡树】

    最近数据结构刚好看到了伸展树,在想这个东西有什么应用,于是顺便学习一下. 二叉查找树(BST),对于树上的任意一个节点,节点的左子树上的关键字都小于这个节点的关键字,节点的右子树上的关键字都大于这个节 ...

  5. HYSBZ 1500 维修数列(伸展树模板)

    题意: 题解:典型伸展树的题,比较全面. 我理解的伸展树: 1 伸展操作:就是旋转,因为我们只需保证二叉树中序遍历的结果不变,所以我们可以旋转来保持树的平衡,且旋转有左旋与右旋.通过这种方式保证不会让 ...

  6. wikioi 1396 伸展树(两个模板)

    题目描写叙述 Description Tiger近期被公司升任为营业部经理.他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来 ...

  7. HDU 2475 Box 树型转线型 + 伸展树

    树型转线型.第一次听说这个概念. . . , 可是曾经已经接触过了,如LCA的预处理部分和树链剖分等.可是没想到还能这么用,三者虽说有不同可是大体思想还是非常相近的,学习了. 推荐博客http://b ...

  8. 伸展树(Splay tree)的基本操作与应用

    伸展树的基本操作与应用 [伸展树的基本操作] 伸展树是二叉查找树的一种改进,与二叉查找树一样,伸展树也具有有序性.即伸展树中的每一个节点 x 都满足:该节点左子树中的每一个元素都小于 x,而其右子树中 ...

  9. HYSBZ - 1588 营业额统计 (伸展树)

    题意:营业额统计 Tiger最近被公司升任为营业部经理,他上任后接受公司交给的第一项任务便是统计并分析公司成立以来的营业情况. Tiger拿出了公司的账本,账本上记录了公司成立以来每天的营业额.分析营 ...

  10. Splay伸展树学习笔记

    Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Ta ...

随机推荐

  1. java.lang.NoClassDefFoundError 异常

    在项目实施过程中,当访问某一个功能时,出现异常为  java.lang.NoClassDefFoundError  com/xxx/yyy/Zzzz > ,检查发现这个类实际已经存在于应用服务器 ...

  2. testng跑失败用例重试

    testng 提高用例通过率,失败用例要重新运行一次 步骤: 1.新建一个Retry 类,implements IRetryAnalyzer接口,这个类里面确定重跑次数,以及分析每次失败是否需要重新运 ...

  3. 跟我一起学extjs5(17--Grid金额字段单位MVVM方式的选择)

    跟我一起学extjs5(17--Grid金额字段单位MVVM方式的选择)         这一节来完毕Grid中的金额字段的金额单位的转换.转换旰使用MVVM特性,整体上和控制菜单的几种模式类似.首先 ...

  4. MySql 跟踪命令

    SHOW ; SHOW FULL PROCESSLIST; ; USE table1; ; SHOW PROFILES; ; SHOW TABLES; SHOW PROFILES; SHOW PROF ...

  5. HTML与CSS入门——第十章 创建用于Web上的图像

    知识点: 1.选择图像软件的方法 2.准备用于网上的照片的方法 3.创建标题和按钮的方法 4.减少图像中颜色数量的方法 5.创建透明图像的方法 6.创建平铺背景的方法 7.创建Web动画的方法 10. ...

  6. js+图片实现图片flash效果

    var pic_width=685; //图片宽度 var pic_height=225; // 图片高度 var button_pos=4; //按扭位置 1左 2右 3上 4下 var stop_ ...

  7. (转)WCF中调用WebService出错,大家帮忙看看,回答就有分

    http://bbs.csdn.net/topics/390542345 在WCF项目里面添加了一个WebService引用,然后在我们调用这个WCF服务时,老出错,提示在 ServiceModel  ...

  8. Android原生Calendar代码阅读(一)

    原生Calendar代码: 5.0Calendar源码.rar 提取的JavaDoc: Calendar的javadoc.rar 1. AsyncQueryService和AsyncQueryServ ...

  9. OkHttp 上手

    OkHttp 上手 优点 快.节省带宽. 支持 HTTP/2 和 SPDY. HTTP/2 和 SPDY 允许对同一个主机的所有请求,使用一个 socket. 如果不支持 SPDY 的话,可以用连接池 ...

  10. TortoiseSVN上次文件显示被锁定

    1.可以使用SVN clean up来清除锁定. 2.如果不是本目录锁定,系统提示上一层目录锁定,需要到上一层或者根目录中清除. 3.如果在根目录下都无法clean的话,一般采取的方法是另外找一个目录 ...