bzoj1861 [Zjoi2006]Book 书架

【题意】
如指令所示:
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本书的编号。


【题解】
伸展树
借助伸展树翻转后中序遍历不变来维护书架上书的顺序。
第一步,用二分的方法建一棵尽量平衡的树。
对于1,(如果不在第1本)把s放在树的最左孩子的左边,它就会是第一个遍历到的,即第1本;
对于2,(如果不在第n本)把s放在树的最右孩子的右边,它会是最后一个遍历到的,即第n本;
对于3,找到与s交换的那一本,硬插在它与它的孩子间。在左边位在它上一本,在右边为在它下一本;
对于4,问s的中序遍历。只需将s转到根,它左孩子的个数+1即为它的中序遍历编号;
对于5,问中序遍历第s的是谁。对于节点x,看看是在它的左子树还是右子树。


【注意与错误】
1、建树时,要注意子树不能包含自己,即build(l,mid-1),build(mid+1,r);
2、书本的实际编号 与 树上节点的编号不同,需要记录其转换关系;
3、(del函数中发现)注意数据转移过程中的顺序,不要删掉了一些后面有用的东西,导致转移中断;
4、(del函数中发现)我们的删除总是把节点旋到根来处理的,记得所有操作都要更改根的标记;
5、(find_abovenum函数中发现)中序遍历也好,求排名也好,要在左子树的数量上+1,否则会出现0;
6、任何树的操作最好放到根来做。否则相关节点的c会不能便捷地求出,因为在树的下部不好更新上部分,除非把更新的节点做一次splay旋到根,同时对整棵树进行更新。


【收获】
可以自制小数据,有针对性的试验某个指令,在合起来测试。避免在一个样例中出现多个不能保证正确性的命令,而无从下手。


【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;


int n;
int a[80010];
struct node
{
	int d,c,f,son[2];
}tr[80010];int len=0,root,pos[80010];//注意树上节点的编号与实际编号的区别  pos[x]实际编号对应的树上的节点编号 


void update(int x)
{
	tr[x].c=tr[tr[x].son[0]].c+tr[tr[x].son[1]].c+1;
}


void clean(int x)
{
	tr[x].c=tr[x].f=tr[x].son[0]=tr[x].son[1]=0;
}


int build(int l,int r)
{
	if(l>r) return 0;
	
	int mid,x;
	mid=(l+r)/2;
	x=++len;
	tr[x].d=a[mid];
	pos[a[mid]]=x;
	
	if(l==r)
	{
		tr[x].son[0]=tr[x].son[1]=0;
		update(x);
		return x;
	}
	
	int lc,rc;
	lc=tr[x].son[0]=build(l,mid-1);
	rc=tr[x].son[1]=build(mid+1,r);
	if(lc!=0) tr[lc].f=x;
	if(rc!=0) tr[rc].f=x;
	
	update(x);
	return x;
}


void rotate(int x,int w)
{
	int f=tr[x].f,ff=tr[f].f;
	int r,R;
	
	r=tr[x].son[w];R=f;
	tr[R].son[1-w]=r;
	if(r!=0) tr[r].f=R;
	
	r=x;R=ff;
	if(tr[ff].son[0]==f) tr[R].son[0]=r;
	else tr[R].son[1]=r;
	tr[r].f=R;
	
	r=f;R=x;
	tr[R].son[w]=r;
	tr[r].f=R;
	
	update(f);
	update(x);
}


void splay(int x,int rt)
{
	while(tr[x].f!=rt)
	{
		int f=tr[x].f,ff=tr[f].f;
		if(tr[f].f==rt)
		{
			if(tr[f].son[0]==x) rotate(x,1);
			else rotate(x,0);
		}
			 if(tr[ff].son[0]==f&&tr[f].son[0]==x){rotate(f,1);rotate(x,1);}
		else if(tr[ff].son[1]==f&&tr[f].son[1]==x){rotate(f,0);rotate(x,0);}
		else if(tr[ff].son[0]==f&&tr[f].son[1]==x){rotate(x,0);rotate(x,1);}
		else if(tr[ff].son[1]==f&&tr[f].son[0]==x){rotate(x,1);rotate(x,0);}
	}
	if(rt==0) root=x;
}


void del(int x)
{
	splay(x,0);
	
		 if(tr[x].son[0]==0&&tr[x].son[1]==0){root=0;clean(x);}
	else if(tr[x].son[0]==0&&tr[x].son[1]!=0){root=tr[x].son[1];tr[root].f=0;clean(x);}
	else if(tr[x].son[0]!=0&&tr[x].son[1]==0){root=tr[x].son[0];tr[root].f=0;clean(x);}
	else if(tr[x].son[0]!=0&&tr[x].son[1]!=0)
	{
		int p=tr[x].son[0];
		while(tr[p].son[1]!=0) p=tr[p].son[1];
		splay(p,x);
		
		int r,R;
		r=tr[x].son[1];R=p;
		tr[R].son[1]=r;
		tr[r].f=R;
		
		root=p;tr[root].f=0;
		clean(x);
		update(p);
	}
}


int find_head()
{
	int x=root;
	while(tr[x].son[0]!=0) x=tr[x].son[0];
	return x;
}


int find_tail()
{
	int x=root;
	while(tr[x].son[1]!=0) x=tr[x].son[1];
	return x;
}


int find_bookid(int k)//查询中序遍历第k的节点编号 
{
	int x=root;
	while(1)
	{
		int lc=tr[x].son[0],rc=tr[x].son[1];
		if(k<=tr[lc].c){x=lc;}
		else if(k>tr[lc].c+1){k-=tr[lc].c+1;x=rc;}
		else break;
	}
	return x;
}


int find_abovenum(int x)//查询x的中序遍历编号
{
	splay(x,0);
	return tr[tr[x].son[0]].c+1;
}


char opt[10];
int main()
{
	int Q;
	scanf("%d%d",&n,&Q);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
	}
	root=build(1,n);
	
	while(Q--)
	{
		int s,t,x,R,r,ans;
		scanf("%s",opt);
		if(opt[0]=='T')
		{
			scanf("%d",&s);
			s=pos[s];
			x=find_head();
			if(x==s) continue;
			del(s);
			splay(x,0);
			
			R=x;r=s;
			tr[R].son[0]=r;
			tr[r].f=R;
			
			update(s);
			update(x);
		}
		else if(opt[0]=='B')
		{
			scanf("%d",&s);
			s=pos[s];
			x=find_tail();
			if(x==s) continue;
			del(s);
			splay(x,0);
			
			R=x;r=s;
			tr[R].son[1]=r;
			tr[r].f=R;
			
			update(s);
			update(x);
		}
		else if(opt[0]=='I')
		{
			scanf("%d%d",&s,&t);
			s=pos[s];
			if(t==-1)
			{
				x=find_bookid(find_abovenum(s)+t);
				del(s);
				splay(x,0);
				
				R=s;r=tr[x].son[0];
				tr[R].son[0]=r;
				tr[r].f=R;
				
				R=x;r=s;
				tr[R].son[0]=r;
				tr[r].f=R;
				
				update(s);
				update(x);
			}
			else if(t==1)
			{
				x=find_bookid(find_abovenum(s)+t);
				del(s);
				splay(x,0);
				
				R=s;r=tr[x].son[1];
				tr[R].son[1]=r;
				tr[r].f=R;
				
				R=x;r=s;
				tr[R].son[1]=r;
				tr[r].f=R;
				
				update(s);
				update(x);
			}
		}
		else if(opt[0]=='A')
		{
			scanf("%d",&s);
			s=pos[s];
			splay(s,0);
			ans=find_abovenum(s)-1;//要减去1,因为求的是上面的书的本数 
			printf("%d\n",ans);
		}
		else if(opt[0]=='Q')
		{
			scanf("%d",&s);
			ans=tr[find_bookid(s)].d;
			printf("%d\n",ans);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值