1. 程式人生 > 實用技巧 >題解 P2596 【[ZJOI2006]書架】

題解 P2596 【[ZJOI2006]書架】

蒟蒻釋出的討論為什麼沒有人理啊!!!!!

題意

讓你寫一種資料結構,可以進行如下操作:

將編號為 \(s\) 的書移動到開頭。
將編號為 \(s\) 的書移動到結尾。
將編號為 \(s\) 的書與其前驅或者後繼交換位置。
詢問編號為 \(s\) 的書前面有幾本書。
詢問位於第 \(s\) 個位置的書的編號。

題解

表示這道題用文藝平衡樹隨便過啊……

但是蒟蒻現在還不會文藝平衡樹,所以怎麼辦?

我們可以考慮給每一個編號附上一個偽權值,然後用平衡樹維護這個偽權值,同時用 \(map\) 記錄偽權值,使得編號和偽權值雙向對映,然後幾種修改操作可以考慮如下實施:

\(top\) 操作就是將原本編號為 \(s\)

的偽權值從樹中刪去,然後在塞入一個比當前書中最小偽權值還小的值作為其新的偽權值。

程式碼如下:

if(opt=="Top")
{
	scanf("%d",&x);
	mp2.erase(mp2.find(mp1[x]));
	FHQ.erase(mp1[x]);
	mp1[x]=--minn;
	mp2[minn]=x;
	FHQ.insert(minn);
}

\(bottom\) 操作同理,塞入一個最大的偽權值

程式碼如下:

if(opt=="Bottom")
{
	scanf("%d",&x);
	mp2.erase(mp2.find(mp1[x]));
	FHQ.erase(mp1[x]);
	mp1[x]=++maxn;
	mp2[maxn]=x;
	FHQ.insert(maxn);
}

\(insert\) 操作就是將一個偽權值的前驅(或後繼)與其交換,由於平衡樹是隻記錄偽權值的,所以我們考慮交換進行對映的 \(map\)

程式碼如下:

if(opt=="Insert")
{
	scanf("%d%d",&x,&t);
	int tmp;
	if(t==0)
	continue;
	if(t==1)
	tmp=mp2[FHQ.get_nxt(mp1[x])];
	else
	tmp=mp2[FHQ.get_pre(mp1[x])];
	swap(mp2[mp1[x]],mp2[mp1[tmp]]);
	swap(mp1[x],mp1[tmp]);
}

但是很顯然,這樣的常數會非常的大,還好本蒟蒻比較臉白,\(AC\) 了。

剩下的比較常規的操作就和完整程式碼一起附上:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
struct fhq_treap
{
	int rt;
	struct Node
	{
		int data,rnd;
		int son[2];
		int size;
	}tr[N];
	int top,num,rub[N];
	int newnode()
	{
		if(top)
		return rub[top--];
		return ++num;
	}
	void up(int p)
	{
		tr[p].size=tr[tr[p].son[0]].size+tr[tr[p].son[1]].size+1;
		return ;
	}
	void split(int p,int &x,int &y,int val)
	{
		if(p==0)
		{
			x=y=0;
			return ;
		}
		if(tr[p].data<val)
		x=p,split(tr[p].son[1],tr[p].son[1],y,val);
		else
		y=p,split(tr[p].son[0],x,tr[p].son[0],val);
		up(p);
		return ;
	}
	int merge(int x,int y)
	{
		if(!x)
		return y;
		if(!y)
		return x;
		if(tr[x].rnd<tr[y].rnd)
		{
			tr[x].son[1]=merge(tr[x].son[1],y);
			up(x);
			return x;
		}
		else
		{
			tr[y].son[0]=merge(x,tr[y].son[0]);
			up(y);
			return y;
		}
	}
	void insert(int x)
	{
		int a,b;
		split(rt,a,b,x);
		int tmp=newnode();
		tr[tmp].data=x;
		tr[tmp].rnd=rand();
		tr[tmp].son[0]=tr[tmp].son[1]=0;
		tr[tmp].size=1;
		tmp=merge(tmp,b);
		rt=merge(a,tmp);
		return ;
	}
	void erase(int x)
	{
		int a,tmp,b;
		split(rt,a,tmp,x);
		split(tmp,tmp,b,x+1);
		rub[++top]=tmp;
		rt=merge(a,b);
		return ;
	}
	int get_rk(int p,int x)
	{
		if(tr[p].data==x)
		return tr[tr[p].son[0]].size+1;
		if(tr[p].data>x)
		return get_rk(tr[p].son[0],x);
		if(tr[p].data<x)
		return tr[tr[p].son[0]].size+1+get_rk(tr[p].son[1],x);
	}
	int get_x(int p,int rk)
	{
		if(tr[tr[p].son[0]].size+1==rk)
		return tr[p].data;
		if(tr[tr[p].son[0]].size+1>rk)
		return get_x(tr[p].son[0],rk);
		if(tr[tr[p].son[0]].size+1<rk)
		return get_x(tr[p].son[1],rk-tr[tr[p].son[0]].size-1);
	}
	int find(int p,int x)
	{
		if(tr[p].data==x)
		return p;
		if(tr[p].data>x)
		return find(tr[p].son[0],x);
		if(tr[p].data<x)
		return find(tr[p].son[1],x);
	}
	int get_pre(int x)
	{
		return get_x(rt,get_rk(rt,x)-1);
	}
	int get_nxt(int x)
	{
		return get_x(rt,get_rk(rt,x)+1);
	}
	void init()
	{
		rt=top=num=0;
		memset(tr,0,sizeof(tr));
		memset(rub,0,sizeof(rub));
	}
}FHQ;
int n,m,x,t;
string opt;
unordered_map<int,int> mp1,mp2;
int maxn,minn;
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		scanf("%d",&x);
		mp1[x]=i;
		mp2[i]=x;
		FHQ.insert(i);
	}
	minn=1,maxn=n;
	for(int i=1;i<=m;++i)
	{
		cin>>opt;
		if(opt=="Top")
		{
			scanf("%d",&x);
			mp2.erase(mp2.find(mp1[x]));
			FHQ.erase(mp1[x]);
			mp1[x]=--minn;
			mp2[minn]=x;
			FHQ.insert(minn);
		}
		if(opt=="Bottom")
		{
			scanf("%d",&x);
			mp2.erase(mp2.find(mp1[x]));
			FHQ.erase(mp1[x]);
			mp1[x]=++maxn;
			mp2[maxn]=x;
			FHQ.insert(maxn);
		}
		if(opt=="Insert")
		{
			scanf("%d%d",&x,&t);
			int tmp;
			if(t==0)
			continue;
			if(t==1)
			tmp=mp2[FHQ.get_nxt(mp1[x])];
			else
			tmp=mp2[FHQ.get_pre(mp1[x])];
			swap(mp2[mp1[x]],mp2[mp1[tmp]]);
			swap(mp1[x],mp1[tmp]);
		}
		if(opt=="Ask")
		scanf("%d",&x),printf("%d\n",FHQ.get_rk(FHQ.rt,mp1[x])-1);
		if(opt=="Query")
		scanf("%d",&x),printf("%d\n",mp2[FHQ.get_x(FHQ.rt,x)]);
	}
}