1. 程式人生 > 其它 >洛谷 P3721 - [AH2017/HNOI2017]單旋(LCT)

洛谷 P3721 - [AH2017/HNOI2017]單旋(LCT)

LCT,阿巴細節題(

洛谷題面傳送門

終於調出來這道題了,寫篇題解(

首先碰到這樣的題我們肯定要考慮每種操作會對樹的形態產生怎樣的影響:

  • 插入操作:對於 BST 有一個性質是,當你插入一個節點時,其在 BST 上的父親肯定是,你把 BST 中父親按權值 sort 一遍排成一列後,在待插入的數的兩側的數對應的節點中,深度較大者。因此我們考慮用一個 set,將所有點的權值和編號壓進去然後在裡面 lower_bound 即可找出待插入點兩側的點。

  • 單旋最小值:稍微畫幾個圖即可發現,對於最小值代表的點 \(x\),如果 \(x\) 已經是根了就可以忽略此次操作,否則假設 \(x\) 在 splay 上的父親為 \(f\)

    ,右兒子為 \(son\),原來的根為 \(rt\),那麼此次操作等價於以下四個刪斷邊操作:

    • 斷開 \(x,f\) 之間的邊
    • 斷開 \(x,son\) 之間的邊(如果 \(x\) 不存在右兒子則忽略)
    • 連上 \(x,rt\) 之間的邊,其中 \(x\)\(rt\) 的父親
    • 連上 \(f,son\) 之間的邊

    注意到這裡涉及刪斷邊,並且在任意時刻圖都是一棵森林,因此可以 LCT 維護。

  • 單旋最大值:同單旋最小值的情況,只不過這裡需要把右兒子改為左兒子

  • 單旋刪除最小值:與單旋最小值的情況類似,只不過這次不需要連 \(x\)\(rt\) 之間的邊

  • 單旋刪除最大值:與單旋刪除最小值的情況類似,只不過這裡也需要把右兒子改為左兒子

程式的大致框架構建出來了,接下來考慮如何具體實現每個操作:

  • 查詢一個點在 BST 上的深度:直接把這個點 access 一遍並轉到 splay 的根,那麼這個點的 siz 就是該點在 BST 上的深度大小。
  • 查詢一個點的左/右兒子:在我們 LCT 的過程中,我們失去了原 BST 上左右兒子的資訊,因此我們無法直接通過將它轉到根,然後呼叫其 ch[0]/ch[1] 的方法求其左右兒子。不過注意到每個點在 BST 上兒子個數 \(\le 2\),因此我們考慮 top tree 的思想,用一個 set 維護其虛兒子,這樣我們每次查詢一個點的左右兒子時,只需把它 access 一遍並轉到根,然後在它的虛兒子集合中找到鍵值大於 / 小於該點的鍵值的點即可。

最後是一些注意點:

  • rotate 時,如果 \(x\) 的父親是 \(x\) 所在 splay 的根,那麼我們要在 \(x\) 父親的父親的虛兒子集合中刪除 \(y\) 加入 \(x\),這一點在普通的 top tree 中不用考慮,因為轉 \(x\) 不會影響 \(x\) 的父親的父親的子樹的大小,但是這裡我們維護的是一個點的虛兒子具體是什麼,虛兒子變了,父親的資訊也要改變。
  • 在查詢左右兒子時,不能找到一個鍵值比待查詢點鍵值大 / 小的點就 return,要在對應子樹中找到深度最淺(中序遍歷中第一位)的點再返回。
const int MAXN=1e5;
const int INF=0x3f3f3f3f;
int ncnt=0;
struct node{int ch[2],f,siz,rev_lz,val;set<int> img_ch;} s[MAXN+5];
void pushup(int k){s[k].siz=s[s[k].ch[0]].siz+s[s[k].ch[1]].siz+1;}
int ident(int k){return ((s[s[k].f].ch[0]==k)?0:((s[s[k].f].ch[1]==k)?1:-1));}
void connect(int k,int f,int op){s[k].f=f;if(~op) s[f].ch[op]=k;}
void rotate(int x){
	int y=s[x].f,z=s[y].f,dx=ident(x),dy=ident(y);
	connect(s[x].ch[dx^1],y,dx);connect(y,x,dx^1);connect(x,z,dy);
	pushup(y);pushup(x);assert(~dx);
	if(dy==-1&&z){
		s[z].img_ch.erase(s[z].img_ch.find(y));
		s[z].img_ch.insert(x);
	}
}
void splay(int k){
	while(~ident(k)){
		if(ident(s[k].f)==-1) rotate(k);
		else if(ident(k)==ident(s[k].f)) rotate(s[k].f),rotate(k);
		else rotate(k),rotate(k);
	}
}
void access(int k){
	int pre=0;
	for(;k;pre=k,k=s[k].f){
		splay(k);
		if(s[k].ch[1]) s[k].img_ch.insert(s[k].ch[1]);s[k].ch[1]=pre;
		if(s[k].ch[1]) s[k].img_ch.erase(s[k].img_ch.find(s[k].ch[1]));
		pushup(k);
	}
}
int findroot(int k){
	access(k);splay(k);
	while(s[k].ch[0]) k=s[k].ch[0];
	splay(k);return k;
}
void link(int x,int y){
	access(x);splay(x);
	s[x].f=y;s[y].img_ch.insert(x);
}//y is x's father
int getfa(int x){
	access(x);splay(x);x=s[x].ch[0];
	while(s[x].ch[1]) x=s[x].ch[1];
	return x;
}
int getls(int x){
	access(x);splay(x);
	for(int c:s[x].img_ch) if(s[c].val<s[x].val){
		while(s[c].ch[0]) c=s[c].ch[0];
		return c;
	}
	return 0;
}
int getrs(int x){
	access(x);splay(x);
	for(int c:s[x].img_ch) if(s[c].val>s[x].val){
		while(s[c].ch[0]) c=s[c].ch[0];
		return c; 
	}
	return 0;
}
void cut(int x,int y){
	access(x);splay(x);int son=s[x].ch[0];
	s[x].ch[0]=s[son].f=0;pushup(x);
}//y is x's father
set<pii> st;
int calc_dep(int x){access(x);splay(x);return s[x].siz;}
void splay_mn(){
	pii p=*++st.begin();int id=p.se;
	access(id);splay(id);printf("%d\n",s[id].siz);
	if(findroot(id)==id) return;
	int fa=getfa(id),rt=findroot(id);
	cut(id,fa);int son=getrs(id);
	if(son) assert(getfa(son)==id),cut(son,id),link(son,fa);
	link(rt,id);assert(findroot(fa)==id);
}
void splay_mx(){
	pii p=*-- --st.end();int id=p.se;
	access(id);splay(id);printf("%d\n",s[id].siz);
	if(findroot(id)==id) return;
	int fa=getfa(id),rt=findroot(id);
	cut(id,fa);int son=getls(id);
	if(son) assert(getfa(son)==id),cut(son,id),link(son,fa);
	link(rt,id);
}
void del_mn(){
	pii p=*++st.begin();int id=p.se;st.erase(st.find(p));
	access(id);splay(id);printf("%d\n",s[id].siz);
	if(findroot(id)==id){
		int son=getrs(id);
		if(son) cut(son,id);
		return;
	}
	int fa=getfa(id),rt=findroot(id);
	cut(id,fa);int son=getrs(id);
	if(son) assert(getfa(son)==id),cut(son,id),link(son,fa);
}
void del_mx(){
	pii p=*-- --st.end();int id=p.se;st.erase(st.find(p));
	access(id);splay(id);printf("%d\n",s[id].siz);
	if(findroot(id)==id){
		int son=getls(id);
		if(son) cut(son,id);
		return;
	}
	int fa=getfa(id),rt=findroot(id);
	cut(id,fa);int son=getls(id);
	if(son) assert(getfa(son)==id),cut(son,id),link(son,fa);
}
int main(){
	int qu;scanf("%d",&qu);
	st.insert(mp(0,0));st.insert(mp(INF,0));
	while(qu--){
		int opt;scanf("%d",&opt);
		if(opt==1){
			int x;scanf("%d",&x);
			s[++ncnt].val=x;s[ncnt].siz=1;
			st.insert(mp(x,ncnt));
			if(st.size()>3){
				pii nxt=*st.upper_bound(mp(x,ncnt));
				pii pre=*--st.lower_bound(mp(x,ncnt));
				int L=(pre.se)?calc_dep(pre.se):0;
				int R=(nxt.se)?calc_dep(nxt.se):0;
				if(L>R) link(ncnt,pre.se);
				else link(ncnt,nxt.se); 
			} printf("%d\n",calc_dep(ncnt));
		} else if(opt==2) splay_mn();
		else if(opt==3) splay_mx();
		else if(opt==4) del_mn();
		else del_mx();
	}
	return 0;
}