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. tab切换jquery代码

    http://immmmm.com/jquery-tab-switch-code-improved.html html <div id="sidebar-tab">   ...

  2. (转)iOS7界面设计规范(9) - UI基础 - 动画

    傍晚下了场大雨,现在坐在屋里也真是很风凉,听着Everlong突然觉得好像去年秋天的气息.每个季节都有各自的气息,每一年也是,如果你留意,便会感觉到.话说这几天,外面的猫猫狗狗们可以补些水来喝了,这也 ...

  3. vuex 模块

    今天,在我编写系统中一个模块功能的时候,由于我使用vuex存储数据的状态,并分模块存储.我是这样在存储文件中定义state,getters,actions,mutations的,我打算在不同模块文件都 ...

  4. 屏蔽掉返回键,menu键,Home键

    public class LockActivity extends Activity{ private static final int FLAG_HOMEKEY_DISPATCHED = 0x800 ...

  5. Three Families

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&p ...

  6. Win32多线程编程(2) — 线程控制

    Win32线程控制只有是围绕线程这一内核对象的创建.挂起.恢复.终结以及通信等操作,这些操作都依赖于Win32操作系统提供的一组API和具体编译器的C运行时库函数.本篇围绕这些操作接口介绍在Windo ...

  7. shell读取文件参数

    环境 csh 说明 通常我们需要使用使用shell脚本处理一些事务,每次调用shell都需要添加参数. 如果重复调用多次这个shell脚本,我们可以将参数存入指定文件,循环得到参数. shell脚本( ...

  8. 深入解析MySQL replication协议

    Why 最开始的时候,go-mysql只是简单的抽象mixer的代码,提供一个基本的mysql driver以及proxy framework,但做到后面,笔者突然觉得,既然研究了这么久mysql c ...

  9. 《第一行代码》学习笔记8-活动Activity(6)

    1.返回上一个活动只需要按一下Back键,还有一个startActivityForResult()方法也是 用于启动活动,这个方法期望在活动销毁时能够返回一个结果给上一个活动. 2.startActi ...

  10. java二维不定长数组测试

    package foxe; import javax.swing.JEditorPane;import javax.swing.JFrame; /** * @author fooxe * * @see ...