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. <php>PDO用法二

    <?php //造PDO对象 $pdo = new PDO("mysql:dbname=mydb;host=localhost","root"," ...

  2. 将Maven项目转换成Eclipse支持的Java项目

    当我们通过模版(比如最简单的maven-archetype-quikstart插件)生成了一个maven的项目结构时,如何将它转换成eclipse支持的java project呢? 1. 定位到mav ...

  3. Mysql--mysqldump命令 备份数据库

    mysqldump命令用来备份数据库. mysqldump命令在DOS的[url=file://\\mysql\\bin]\\mysql\\bin[/url]目录下执行. 1) 导出整个数据库(导出文 ...

  4. magento xml配置详解

    <?XML版本=“1.0”? <config> <节> 实施例translate="label"> <label>的一个例子< ...

  5. js点击打开弹窗

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  6. SQL函数简述

    数字函数ABS 取绝对值 POWER 乘方 LN 10为底数取幂SQRT 平方根 EXP e的n次乘方 LOG(m,n) m为底数n取幂数学运算函数:ACOS ATAN ATAN2 COS COSH ...

  7. 伸缩放大的js

    window.onload = function() {         var div1 = document.getElementById('div1');         div1.onmous ...

  8. Windows命令行(DOS命令)教程-6 (转载)http://arch.pconline.com.cn//pcedu/rookie/basic/10111/15325_5.html

    8. type [功能] 在屏幕上显示文本文件内容命令 [格式] type [C:][path]filename.ext [说明] type命令用来在屏幕上快速.简便地显示文本文件的内容,扩展名为TX ...

  9. 查看kafka的group.id

    kafka/config目录下的consumer.properties中可以看到

  10. Tomcat 默认应用

    在部署应用时需要更改默认的端口号及应用,以免让别人知道使用的服务器类型而进行攻击.tomca的部署有多种方式,这里简单谈一下.目前想到有三种方式:一.添加 Context在Tomcat的配置文件中,一 ...