【Splay】【ZJOI2006】书架

【题目描述】

小T有一个很大的书柜。这个书柜的构造有些独特,即书柜里的书是从上至下堆放成一列。她用1到n的正整数给每本书都编了号。 
小T在看书的时候,每次取出一本书,看完后放回书柜然后再拿下一本。由于这些书太有吸引力了,所以她看完后常常会忘记原来是放在书柜的什么位置。不过小T的记忆力是非常好的,所以每次放书的时候至少能够将那本书放在拿出来时的位置附近,比如说她拿的时候这本书上面有X本书,那么放回去时这本书上面就只可能有X-1、X或X+1本书。 
当然也有特殊情况,比如在看书的时候突然电话响了或者有朋友来访。这时候粗心的小T会随手把书放在书柜里所有书的最上面或者最下面,然后转身离开。 
久而久之,小T的书柜里的书的顺序就会越来越乱,找到特定的编号的书就变得越来越困难。于是她想请你帮她编写一个图书管理程序,处理她看书时的一些操作,以及回答她的两个提问: 
(1)编号为X的书在书柜的什么位置; 
(2)从上到下第i本书的编号是多少。

【输入格式】

第一行有两个数n,m,分别表示书的个数以及命令的条数;第二行为n个正整数:第i个数表示初始时从上至下第i个位置放置的书的编号;第三行到m+2行,每行一条命令。命令有5种形式: 
1.Top S——表示把编号为S的书房在最上面。 
2.Bottom S——表示把编号为S的书房在最下面。 
3.Insert S T——T∈{-1,0,1},若编号为S的书上面有X本书,则这条命令表示把这本书放回去后它的上面有X+T本书; 
4.Ask S——询问编号为S的书的上面目前有多少本书。 
5.Query S——询问从上面数起的第S本书的编号。 

【输出格式】

对于每一条Ask或Query语句你应该输出一行,一个数,代表询问的答案。


平衡树的操作,同时存在编号与位置

考虑根据位置建立平衡树,并用pos[x]表示编号为x的书对应平衡树中的哪一个点

Top操作将左子树接到右边前驱

Bottom操作相反

Insert操作如果t=0,就不管,否则与前驱或后继交换编号,并更新编号对应点

Ask操作将pos[x]旋转至根,输出左子树size即可

Query操作find(x)输出其编号即可

#include<iostream>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
int n,m,root,pos[80005];
char op[10];
struct Tree
{
	int ch[2],fa,v;
	int size;
}tree[80005];
void push_up(int k)
{
	tree[k].size=tree[tree[k].ch[0]].size+tree[tree[k].ch[1]].size+1;
	pos[tree[tree[k].ch[0]].v]=tree[k].ch[0],pos[tree[tree[k].ch[1]].v]=tree[k].ch[1];
}
void build(int l,int r,int fa)
{
	if(l>r) return ;
	int mid=(l+r)>>1;
	tree[mid].size=1;
	tree[mid].fa=fa;
	if(mid<fa) tree[fa].ch[0]=mid;
	else tree[fa].ch[1]=mid;
	if(l==r) return ;
	build(l,mid-1,mid);
	build(mid+1,r,mid);
	push_up(mid);
}
int whoson(int x)
{
	return (tree[tree[x].fa].ch[0]==x?0:1);
}
void connect(int x,int fa,int son)
{
	tree[x].fa=fa;
	tree[fa].ch[son]=x;
}
void rotate(int x)
{
	int y=tree[x].fa,mroot=tree[y].fa;
	int yson=whoson(x),mrootson=whoson(y);
	int B=tree[x].ch[yson^1];
	connect(B,y,yson);
	connect(y,x,yson^1);
	connect(x,mroot,mrootson);
	push_up(y);
	push_up(x);
}
void Splay(int x,int tp)
{
	while(tree[x].fa!=tp)
	{
		int y=tree[x].fa,z=tree[y].fa;
		if(z!=tp)
		{
			if(whoson(x)^whoson(y)) rotate(x);
			else rotate(y);
		}
		rotate(x);
	}
	pos[tree[x].v]=x;
	if(!tp) root=x;
}
int find(int x)
{
	if(x>n||x<1) return 0;
	int now=root;
	while(1)
	{
		int lsum=tree[tree[now].ch[0]].size;
		if(lsum>=x) now=tree[now].ch[0];
		else if(lsum+1==x) return now;
		else x-=lsum+1,now=tree[now].ch[1];
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&tree[i].v),pos[tree[i].v]=i;
	build(1,n,0);
	root=(n+1)>>1;
	for(int i=1;i<=m;i++)
	{
		scanf("%s",op);
		if(op[0]=='T')
		{
			int x,y;
			scanf("%d",&x);
			x=pos[x];
			Splay(x,0);
			if(!tree[x].ch[0]) continue;
			if(!tree[x].ch[1])
			{
				tree[x].ch[1]=tree[x].ch[0];
				tree[x].ch[0]=0;
				continue;
			}
			y=find(tree[tree[x].ch[0]].size+2);
			Splay(y,x);
			int lig=tree[x].ch[0];
			tree[lig].fa=y;
			tree[y].ch[0]=lig;
			tree[x].ch[0]=0;
			push_up(y);
		}
		else if(op[0]=='B')
		{
			int x,y;
			scanf("%d",&x);
			x=pos[x];
			Splay(x,0);
			if(!tree[x].ch[1]) continue;
			if(!tree[x].ch[0])
			{
				tree[x].ch[0]=tree[x].ch[1];
				tree[x].ch[1]=0;
				continue;
			}
			y=find(tree[tree[x].ch[0]].size);
			Splay(y,x);
			int rig=tree[x].ch[1];
			tree[rig].fa=y;
			tree[y].ch[1]=rig;
			tree[x].ch[1]=0;
			push_up(y);
		}
		else if(op[0]=='I')
		{
			int x,t,y;
			scanf("%d%d",&x,&t);
			if(!t) continue;
			x=pos[x];
			Splay(x,0);
			y=find(tree[tree[x].ch[0]].size+1+t);
			pos[tree[x].v]=y,pos[tree[y].v]=x;
			swap(tree[x].v,tree[y].v);
		}
		else if(op[0]=='A')
		{
			int x;
			scanf("%d",&x);
			x=pos[x];
			Splay(x,0);
			printf("%d\n",tree[tree[x].ch[0]].size);
		}
		else
		{
			int x;
			scanf("%d",&x);
			x=find(x);
			printf("%d\n",tree[x].v);
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值