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. 关于bootstrap--表格(tr的各种样式)

    只需要<tr class="active">就可以用active样式. 特别提示:除了”.active”之外,其他四个类名和”.table-hover”配合使用时,Bo ...

  2. dbda封装类(包括:返回二维数组、Ajax调用返回字符串、Ajax调用返回JSON)

    <?php class DBDA { public $host = "localhost"; public $uid = "root"; public $ ...

  3. Redhat6.4 配置本地网络的FTP YUM源

    Redhat6.4 配置本地网络的FTP YUM源 如果本机IP: 192.168.8.47 (一) 配置本机的yum源 使用以下的方法能够配置本机的yum源: 1) scp命令上传ISO文件到: / ...

  4. UVA 825 Walking on the Safe Side(记忆化搜索)

      Walking on the Safe Side  Square City is a very easy place for people to walk around. The two-way ...

  5. iOS 8 Auto Layout界面自动布局系列5-自身内容尺寸约束、修改约束、布局动画

    首先感谢众多网友的支持,最近我实在是事情太多,所以没有写太多.不过看到大家的反馈和评价,我还是要坚持挤出时间给大家分享我的经验.如果你对我写的东西有任何建议.意见或者疑问,请到我的CSDN博客留言: ...

  6. MySQL Replication Error 处理一例

    故障现象 MySQL slave status详情 mysql> show slave status\G *************************** 1. row ********* ...

  7. css设置滚动条颜色与样式以及如何去掉与隐藏滚动条

    我们大家在浏览网页的时偶尔会看到很漂亮的各种颜色样式的滚动条,这就是通过css代码控制来实现的,于是本人搜集整理一番,这里和大家分享一下使用CSS设置滚动条颜色以及如何去掉滚动条的方法,需要的朋友可以 ...

  8. EClipse开发NDK流程

    EClipse开发NDK流程(现在studio也在2.2之后支持了非常简单,只要创建项目的时候勾选c++支持就可以了)   什么情况下使用ndk,1.保护代码,java很容易反编译,c/c++反汇编比 ...

  9. Gradle 编译时选择不同的 google-services.json

    在做的安卓应用需要在 debug 和 release build中使用不同的谷歌服务账号,要用到不同的google-serivces.json ,手动替换的话太费时费力,好在万能的gradle可以完成 ...

  10. IOS 请求服务器的方式

    IOS 中请求服务器的方式主要有Get 和Post . Get :[1]向服务器发索取数据的一种请求; [2]获取信息,而不是修改信息,类似数据库查询功能一样,数据不会被修改;请求的参数会跟在url后 ...