BZOJ1861: [Zjoi2006]Book 書架 Splay

BZOJ1861: [Zjoi2006]Book 書架

Time Limit: 4 Sec  Memory Limit: 64 MB
Submit: 1302  Solved: 747

題解:Splay

對於兩種詢問操作不多說。
對於剩下的幾種修改,本質上是一模一樣的,首先要判斷如果是Insert而且T==0那麼就continue
然後先把x點從樹中刪除,具體刪除方法是把區間[x,x]Splay出來然後把ls[x+1]斷開
然後就是插入,把所要找的位置的區間[x,x+1]splay出來,此時ls[x+1]爲empty,然後直接把節點接到x+1就可以了(其實對於Bottom和Top可以簡化一些代碼見)
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=100005;
int ls[N],rs[N],siz[N],fa[N],a[N],n,m,root,S,T;
char c[11];
void Pushup(int x)
{
	siz[x]=siz[ls[x]]+siz[rs[x]]+1;
}
void print()
{
	cout<<"root="<<root<<endl;
	for(int i=0;i<=n+2;i++) printf("i=%d ls=%d rs=%d fa=%d siz=%d\n",i,ls[i],rs[i],fa[i],siz[i]);
} 
void Rotate(int x,int &k)
{
    int y=fa[x],z=fa[y];
    if(k==y) k=x;
    else
    {
        if(y==ls[z]) ls[z]=x;
        else rs[z]=x;
    }
    fa[x]=z;
    fa[y]=x;
    if(ls[y]==x)
    {
        ls[y]=rs[x];
        fa[rs[x]]=y;
        rs[x]=y;
    }
    else
    {
        rs[y]=ls[x];
        fa[ls[x]]=y;
        ls[x]=y;
    }
    Pushup(y);
    Pushup(x);
}
void Splay(int x,int &k)
{
	while(x!=k)
	{
		int y=fa[x],z=fa[y];
		if(y!=k)
		{
			if(ls[z]==y^ls[y]==x) Rotate(x,k);
			else Rotate(y,k);
		}
		Rotate(x,k);
	}
}
void Build(int l,int r,int f)
{
	if(l>r) return;
	if(l==r)
	{
		siz[a[l]]=1;
		fa[a[l]]=a[f];
		if(l<f) ls[a[f]]=a[l];
		else rs[a[f]]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	Build(l,mid-1,mid);
	Build(mid+1,r,mid);
	Pushup(a[mid]);
	fa[a[mid]]=a[f];
	if(mid<f) ls[a[f]]=a[mid];
	else rs[a[f]]=a[mid];
}
int tot; 
int Getnum(int x,int rank)
{
	if(rank==siz[ls[x]]+1) return x;
	else if(rank<=siz[ls[x]]) return Getnum(ls[x],rank);
	else return Getnum(rs[x],rank-siz[ls[x]]-1);
}
int Getrank(int x)
{
	Splay(x,root);
	return siz[ls[x]]+1;
}
void Del(int x)
{
	int tmp=Getrank(x);
	int y=Getnum(root,tmp-1),z=Getnum(root,tmp+1);
	Splay(y,root);
	Splay(z,rs[y]);
	ls[z]=fa[x]=0;
	Pushup(z);
}
void Ins(int nu,int x)
{
	int u=Getnum(root,nu);
	int v=Getnum(root,nu+1);
	Splay(u,root);
	Splay(v,rs[u]);
	ls[v]=x;
	fa[x]=v;
	Pushup(v);
}
void Top(int x)
{
	int u=Getnum(root,2);
	Splay(u,root);
	fa[S]=x,ls[x]=S;
	fa[x]=u,ls[u]=x;
	Pushup(S),Pushup(x),Pushup(u);
}
void Bot(int x)
{
	int u=Getnum(root,n);
	Splay(u,root);
	fa[T]=x,rs[x]=T;
	fa[x]=u,rs[u]=x;
	Pushup(T),Pushup(x),Pushup(u);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n+1;i++) scanf("%d",&a[i]);
	S=n+1,T=n+2;
	a[1]=S,a[n+2]=T;
	root=a[(n+3)>>1];
	Build(1,n+2,0);
	int x,y,u,v;
	while(m--)
	{
		scanf("%s%d",c,&x);
		if(c[0]=='Q') printf("%d\n",Getnum(root,x+1));
		else if(c[0]=='A') printf("%d\n",Getrank(x)-2);
		else if(c[0]=='I')
		{
			scanf("%d",&y);
			if(y==0) continue;
			int tmp=Getrank(x);
			Del(x);
			if(y==-1) Ins(tmp-2,x);
			if(y==1) Ins(tmp,x);
		}
		else
		{
			Del(x);
			if(c[0]=='T') Top(x);
			if(c[0]=='B') Bot(x);
		}
	}
}


Description

小T有一個很大的書櫃。這個書櫃的構造有些獨特,即書櫃裏的書是從上至下堆放成一列。她用1到n的正整數給每本書都編了號。 小T在看書的時候,每次取出一本書,看完後放回書櫃然後再拿下一本。由於這些書太有吸引力了,所以她看完後常常會忘記原來是放在書櫃的什麼位置。不過小T的記憶力是非常好的,所以每次放書的時候至少能夠將那本書放在拿出來時的位置附近,比如說她拿的時候這本書上面有X本書,那麼放回去時這本書上面就只可能有X-1、X或X+1本書。 當然也有特殊情況,比如在看書的時候突然電話響了或者有朋友來訪。這時候粗心的小T會隨手把書放在書櫃裏所有書的最上面或者最下面,然後轉身離開。 久而久之,小T的書櫃裏的書的順序就會越來越亂,找到特定的編號的書就變得越來越困難。於是她想請你幫她編寫一個圖書管理程序,處理她看書時的一些操作,以及回答她的兩個提問:(1)編號爲X的書在書櫃的什麼位置;(2)從上到下第i本書的編號是多少。

Input

第一行有兩個數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本書的編號。

Output

對於每一條Ask或Query語句你應該輸出一行,一個數,代表詢問的答案。

Sample Input

10 10
1 3 2 7 5 8 10 4 9 6
Query 3
Top 5
Ask 6
Bottom 3
Ask 3
Top 6
Insert 4 -1
Query 5
Query 2
Ask 2

Sample Output

2
9
9
7
5
3

HINT

數據範圍


100%的數據,n,m < = 80000

 

Source

[Submit][Status][Discuss]
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章