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. javascript对URL中的参数进行简单加密处理

    javascript的api本来就支持Base64,因此我们可以很方便的来进行编码和解码. var encodeData = window.btoa("name=xiaoming&a ...

  2. OpenXML - 如何导出List<DataModel>到Excel -- Part 1

    最近这几天研究OpenXML: 这是Open XML的一些介绍: Open XML 介绍:http://baike.baidu.com/view/1201978.htm 下载:http://www.m ...

  3. eclipse 集成maven插件

    本文转载自:http://www.blogjava.net/fancydeepin/archive/2012/07/13/eclipse_maven3_plugin.html 环境准备: eclips ...

  4. MDM 证书申请流程(vendor及customer)

    整个流程分为两部分:vendor,customer. 一.Vendor 1.成为一个 MDM Vendor 1) 首先你须要拥有一个 Apple Enterprise account($299/年). ...

  5. 2.4 Git 基础 - 撤消操作

    2.4 Git 基础 - 撤消操作 撤消操作 任何时候,你都有可能需要撤消刚才所做的某些操作.接下来,我们会介绍一些基本的撤消操作相关的命令.请注意,有些撤销操作是不可逆的,所以请务必谨慎小心,一旦失 ...

  6. QiniuUpload- 一个方便用七牛做图床然后插入markdown的小工具

    最近一段时间有用markdown做笔记,其他都好,但是markdown插入图片挺麻烦的,特别是想截图之后直接插入的时候.需要首先把图片保存了,然后还要上传到一个地方生成链接才能插入.如果有个工具可以直 ...

  7. 【精品】Android游戏类源码大集合

    Android自定义效果——随机抽奖 一个模拟抽奖的效果,用户设定若干个选项,添加之后,就可以通过程序,来帮助随机选择其中一项出来.这个类似超市里面那种指针转盘抽奖,run之后是一个动态效果图,初始快 ...

  8. NVL函数(NVL,NVL2,NULLIF,COALESCE)

    NVL 语法:NVL( expr1, expr2) 功能:如果expr1为NULL,则NVL函数返回expr2的值,否则返回expr1的值,如果两个参数的都为NULL ,则返回NULL. 注意事项:e ...

  9. thinking in java知识小记(一)

    知识点一(javadoc): 使用javadoc时特别注意选择encoding和charset为utf-8,要不然生成的javadoc会是乱码,命令:javadoc -encoding utf-8 - ...

  10. Chinese_PRC_CI_AS and SQL_Latin1_General_CP1_CI_AS类型错误

    在编写存储过程时,经常会用到临时表,而且往往会使用临时表与正式表相关联. 当临时表与正式表关联的字段是字符类型的时候,因为临时表创建的字符类型会默认数据库的编码,而正式表的字符编码可能不同,那么这个时 ...