Splay Tree

\(Splay Tree\)

\(Splay\)是一种非常诡异的数据结构

核心:二叉搜索树

优化:复杂度均摊\(O(nlog n)\)

优化操作:Splay操作

在刚学\(Splay\)时不建议看它的势能分析,因为并没有什么卵用

引入

二叉搜索树(\(BST,Binary Search Tree\)):

核心性质:左儿子小于自己,右儿子大于自己的一棵二叉树

缺陷:对于不同序列树高会呈现$log\ n - n $

\(rotate\)操作

核心:保持BST的大小关系,改变父子关系的一种操作

Splay.png

一个正常的BST局部

由父子关系得到 \(c<x<b<y<a<z\)

rotate后

Splay.png

可以看到上面的关系依然成立,并且x变成了y的父亲

这样的rotate操作其实取决于被rotate的节点x是y的左儿子还是右儿子,但是两种情况对称,写起来就是

void rotate(int u){
	int f=fa[u],ff=fa[fa[u]],d=son[f][0]==u?0:1,df=son[ff][0]==f?0:1;
	son[ff][df]=u;
	son[f][d]=son[u][!d];
	fa[son[u][!d]]=f;
	son[u][!d]=f;
	fa[u]=ff,fa[f]=u;
}

\[\ \]

\[\ \]

\(Splay\)操作

经典的Splay操作有很多分类讨论,这里我们介绍一种精简一点的版本

Splay(u,to),将\(u\)旋转到\(to\)节点的儿子

并且途中经过的链链长减半(特别的,当\(to\)\(0\)时,即旋转到根)

void Splay(int u,int to){
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(fa[fa[u]]!=to) {
			if((son[f][0]==u)^(son[ff][0]==f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(to==0) rt=u;
}

\(u\)的祖父不是\(to\)时,考虑两种情况:你与你父亲的作为儿子方向相同和不同(作成图后,可以看到是之字形和一字形)

相同时,如果我们直接多次\(rotate(u)\),会将原来那条\(u\)\(ff\)的链保留,也就是说,仍然存在存在原来链长,所以我们先\(rotate(f)\),就解决了

另一种就直接两次\(rotate(u)\)即可

\[\ \]

\[\ \]

\[\ \]

\(Splay Tree\)基础操作介绍

事实上\(Splay\)相比起其他平衡树做起一些奇怪的操作要方便的多,反正干什么你都直接\(Splay\)就是了

\(Insert\)操作

插入一个权值为x的点,保证没有重复

void Insert(int x){
    int u=rt;
    if(!u) {
        rt=++cnt,val[rt]=x;
        return;
    }
    while(son[u][x>val[u]]) u=son[u][x>val[u]];
    son[u][x>val[u]]=++cnt,val[cnt]=x;
    Splay(cnt,0);
}

注意最后的\(Splay\)操作保证了复杂度

Find_Next操作

插叙一个节点\(x\)的前驱和后继

int Find_Next(int x,int d){
    Splay(x,0);
    int u=son[x][d];
    if(!u) return -1;//不存在
    while(son[u][!d]) u=son[u][!d];
    Splay(u,0);//很关键
    return u;
}

\(Delete\)操作

将节点编号为\(x\)的点删除

void Del(int x){
   	Splay(x,0);
    if(!son[x][0]) {
        rt=son[x][1];
        fa[rt]=0;
        return;
    }//如果没有前驱,直接删除
    int u=son[x][0];
    while(son[u][1]) u=son[u][1];//找到x的前驱
    //将前驱Splay到x后,前驱一定是左子树中最大的,它没有右儿子,所以直接将右儿子接上去就可以了
    Splay(u,x);
    son[u][1]=son[x][1];
    fa[son[x][1]]=u;
    rt=u;
}

如果觉得我的代码有问题,请尽快联系我

\[\ \]

\[\ \]

\(Splay\)使用的一些注意事项

1.\(Splay\)的本质依然只是一个\(BST\),所以\(BST\)能干的事它都能干

2.\(Splay\)常数大概是11倍左右,但是跑不满(\(LCT\)是跑满的...)

3.\(Splay\)不建议与其他数据结构嵌套

4.100000以上的数据使用\(Splay\)要小心

5.\(Splay\)操作的不同实现可能对常数有着很大影响

学了一些基本操作,我们搞搞模板题

T1 营业额统计

经典裸题,题意求\(a[1]+\sum _{i=2}^{i<=n} min_{j=1}^{j<i}\{ abs(a[i]-a[j]) \}\)

插入,求前驱和后继即可(我为什么不写set...)

注意学习一个新的数据结构时要有耐心,慢慢调...

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e5+10,INF=1e9+10;

int n,m;
int rt,fa[N],son[N][2],sz[N],val[N],c[N],cnt;
void Up(int u){
	sz[u]+=sz[son[u][0]]+sz[son[u][1]];
}
void rotate(int u){
	int f=fa[u],ff=fa[fa[u]],d=son[f][0]==u?0:1,df=son[ff][0]==f?0:1;
	son[ff][df]=u;
	son[f][d]=son[u][!d];
	fa[son[u][!d]]=f;
	son[u][!d]=f;
	fa[u]=ff,fa[f]=u;
	Up(f),Up(u);
	Up(ff);
}

void Splay(int u,int to){
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(fa[fa[u]]!=to) {
			if((son[f][0]==u)^(son[ff][0]==f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(to==0) rt=u;
}

void Find(int x){
	int u=rt;
	while(son[u][x>val[u]]&&val[u]!=x) u=son[u][x>val[u]];
	Splay(u,0);
}

//0 pre 1 nxt
int Find_Next(int x,int k){
	Find(x);
	if(val[rt]<=x&&!k) return val[rt];
	if(val[rt]>=x&&k) return val[rt];
	int v=son[rt][k];
	if(!v) return INF;
	while(son[v][!k]) v=son[v][!k];
	return val[v];
}

void Insert(int x){
	if(!rt) {
		rt=++cnt;
		son[cnt][0]=son[cnt][1]=0,c[cnt]=1,sz[cnt]=1;val[cnt]=x;
		return;
	}
	Find(x);
	if(val[rt]==x) {
		c[rt]++;
		return; 
	}
	int u=rt;
	while(son[u][x>val[u]]) u=son[u][x>val[u]];
	son[u][x>val[u]]=++cnt;
	fa[cnt]=u,son[cnt][0]=son[cnt][1]=0,c[cnt]=1,sz[cnt]=1;val[cnt]=x;
	Splay(cnt,0);
}


int main(){
	ll ans=0;
	rep(i,1,n=rd()) {
		int x=rd();
		if(i==1) ans+=x;
		else {
			int pre=Find_Next(x,0);
			int nxt=Find_Next(x,1);
			ans+=min(abs(pre-x),abs(nxt-x));
		}
		Insert(x);
	}
	printf("%lld\n",ans);
}



\[\ \]

T2 郁闷的出纳员

这题不需要区间操作

插入整体标记,第k大查询

查询第k大操作需要我们存储一个\(size\)值,\(cnt\)表示重复出现的次数

注意在\(Splay\)的时候要\(Up\)

第一次打第k大查询很有可能挂,注意每次\(while\)下去都必须\(Splay\)上来

这个删除操作比较奇怪,建议自己实现一下

void Up(int u){
    if(!u) return;
    sz[u]=sz[son[u][0]]+sz[son[u][1]];
}
#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)



char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e5+10,INF=2e9+10,P=1e9+7;




int n,lim,d;
int rt,fa[N],son[N][2],sz[N],val[N],c[N],cnt;

void Up(int u) {
	if(!u) return;
	sz[u]=c[u]+sz[son[u][0]]+sz[son[u][1]];
}

void rotate(int u){
	int f=fa[u],ff=fa[f],d=(son[f][1]==u);
	son[ff][son[ff][1]==f]=u;
	son[f][d]=son[u][!d];
	fa[son[u][!d]]=f;
	son[u][!d]=f;
	fa[f]=u;
	fa[u]=ff;
	Up(f),Up(u),Up(ff);
}


void Splay(int u,int to){
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if((son[f][1]==u)^(son[ff][1]==f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

void Insert(int x){
	if(!rt) {
		rt=++cnt;
		son[cnt][0]=son[cnt][1]=0,c[cnt]=1,sz[cnt]=1;val[cnt]=x;
		return;
	}
	int u=rt;
	while(son[u][x>val[u]] && val[u]!=x) u=son[u][x>val[u]];
	if(val[u]==x) {
		c[u]++;
		sz[u]++;
		Splay(u,0);
		return; 
	}
	son[u][x>val[u]]=++cnt;
	fa[cnt]=u,son[cnt][0]=son[cnt][1]=0,c[cnt]=1,sz[cnt]=1;val[cnt]=x;
	Splay(cnt,0);
}

int ans;
void Del(){
	int u=rt;
	while(u){
		if(son[u][0] && val[u]+d<=lim){
			ans+=sz[son[u][0]];
			son[u][0]=0;
		}
		if(val[u]+d<lim) {
			ans+=c[u];
			if(u==rt){ 
				fa[son[u][1]]=0;
				rt=son[u][1];
				u=son[u][1];
				continue;
			} else {
				fa[son[u][1]]=fa[u];
				son[fa[u]][son[fa[u]][1]==u]=son[u][1];
				if(!son[u][1]) {
					if(fa[u]) Splay(fa[u],0);
					break;
				}
				u=son[u][1];
				continue;
			}
		}
		if(son[u][0]) u=son[u][0];
		else {
			Splay(u,0);
			break;
		}
	}
}

int Quekth(int k) {
	if(sz[rt]<k) return -1;
	int u=rt;
	while(u) {
		if(sz[son[u][1]]>=k) u=son[u][1];
		else {
			k-=sz[son[u][1]];
			if(c[u]>=k) {
				Splay(u,0);
				return val[u]+d;
			}
			k-=c[u];
			u=son[u][0];
		}
	}
	return -1;
}

char opt[20];

int main(){
	n=rd(),lim=rd();
	rep(i,1,n) {
		scanf("%s",opt);
		int x=rd();
		if(opt[0]=='I') {
			if(x<lim) continue;
			x-=d;
			Insert(x);
		} else if(opt[0]=='A') {
			d+=x;
		} else if(opt[0]=='S') {
			d-=x;
		} else if(opt[0]=='F') {
			printf("%d\n",Quekth(x));
		}
		Del();
	}
	printf("%d\n",ans);
}




\[\ \]

\[\ \]

T3 宠物收养所

插入,查询前驱后继,删除操作

注意答案要取模

#include<bits/stdc++.h>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}
const int N=1e5+10,INF=2e9+10,P=1e9+7;
int n,d;
int rt,fa[N],son[N][2],val[N],c[N],cnt;
void rotate(int u){
	int f=fa[u],ff=fa[f],d=(son[f][1]==u);
	son[ff][son[ff][1]==f]=u;
	son[f][d]=son[u][!d];
	fa[son[u][!d]]=f;
	son[u][!d]=f;
	fa[f]=u;
	fa[u]=ff;
}

void Splay(int u,int to){
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if((son[f][1]==u)^(son[ff][1]==f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

void Insert(int x){
	if(!rt) {
		rt=++cnt;
		son[cnt][0]=son[cnt][1]=0,c[cnt]=1;val[cnt]=x;
		return;
	}
	int u=rt;
	while(son[u][x>val[u]] && val[u]!=x) u=son[u][x>val[u]];
	if(val[u]==x) {
		c[u]++;
		Splay(u,0);
		return; 
	}
	son[u][x>val[u]]=++cnt;
	fa[cnt]=u,son[cnt][0]=son[cnt][1]=0,c[cnt]=1;val[cnt]=x;
	Splay(cnt,0);
}

ll ans;
void Find(int x) {
	int u=rt;
	while(val[u]!=x && son[u][x>val[u]]) u=son[u][x>val[u]];
	Splay(u,0);
}

int Find_Next(int x,int k){
	Find(x);
	if(val[rt]==x||((val[rt]<x)^k)) return rt;
	int u=son[rt][k];
	while(u && son[u][!k]) u=son[u][!k];
	return u;
}

void Del(int x){
	c[x]--;
	if(c[x]) return;
	int u=son[x][0],f=fa[x];
	if(!u) {
		fa[son[x][1]]=f;
		son[f][son[f][1]==x]=son[x][1];
		if(x==rt) rt=son[x][1];
		return;
	}
	while(son[u][1]) u=son[u][1];
	Splay(u,x);
	son[u][1]=son[x][1];
	fa[son[x][1]]=u;
	fa[u]=f;
	son[f][son[f][1]==x]=u;
	if(x==rt) rt=u;
}
int main(){
	n=rd();
	rep(i,1,n) {
		int k=rd(),x=rd();
		if(!rt||k==d) {
			Insert(x);
			d=k;
			continue;
		}
		int pre=Find_Next(x,0),nxt=Find_Next(x,1);
		if(!pre || (nxt && val[nxt]-x<x-val[pre])) {
			ans+=val[nxt]-x;
			Del(nxt);
		} else {
			ans+=x-val[pre];
			Del(pre);
		}
	}
	printf("%lld\n",ans%1000000);
}




写到这里,我们对于\(Splay\)有了一些基础认识,可以来学习一些新的操作了

\(Splay\)区间更新,区间翻转

ll Addmark[N],sum[N];//区间加标记
void Down(int u){
    if(!u) return;
    Addmark[son[u][0]]+=Addmark[u];
    Addmark[son[u][1]]+=Addmark[u];
    sum[son[u][0]]+=sz[son[u][0]]*Addmark[u];
    sum[son[u][1]]+=sz[son[u][1]]*Addmark[u];
}

我们先来学习经典的\(Down\)操作。。

\(Splay\)上的\(Down\)要稍微注意一下,有两种情况是必须要\(Down\)下去的

1.父子关系发生改变时(即\(rotate\)时)

2.查询节点权值时

再算上\(Up\)操作,我的\(Splay\)函数会变成这样

void rotate(int u) {
	int f=fa[u],ff=fa[f],d=(son[f][1]==u),df=(son[ff][1]==f);
    Down(ff),Down(f),Down(u);
	son[ff][df]=u,fa[u]=ff;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	son[u][!d]=f,fa[f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
    Down(u);
    while(fa[u]!=to) {
       	int f=fa[u],ff=fa[f]; 
        if((son[f][1]==u)^(son[ff][1]==f)) rotate(u);
        else rotate(f);
    }
    if(!to) rt=u;
}

(但是经过严谨推导后,其实我们可以发现一些\(Up\)\(Down\)是没有必要的,但是我们先打暴力嘛)

关于如何区间修改

\(l-1 \ Splay\)到根,再将\(r+1 \ Splay\)到根下面,这样的话,我们要求的区间就会汇集在子树\(son[son[rt][1]][0]\)

对于边界问题,当然可以打特判,不过建立两个哨兵会方便一些


void Upd(int l,int r,int x){
	if(l==1&&r==n) {
		sum[rt]+=1ll*x*sz[rt];
		t[rt]+=x;
		val[rt]+=x;
		return;
	}
	if(l>1) Splay(l-1,0);
	if(r<n) {
		Splay(r+1,l-1);
		sum[son[r+1][0]]+=x*sz[son[r+1][0]];
		val[son[r+1][0]]+=x;
		t[son[r+1][0]]+=x;
		Splay(son[r+1][0],0);
		return;
	}
	sum[son[rt][1]]+=sz[son[rt][1]];
	t[son[rt][1]]+=x;
	val[son[rt][1]]+=x;
	Splay(son[rt][1],0);
}

这个是打了特判的版本

翻转操作也类似,就不再赘述了

来我们做一道\(Splay\)(线段树)裸题

T4 A Simple Problem with Integers

由于这份代码是我第一次打的(太傻帽了),不建议参考,对拍还是可以的

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<iostream>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e5+10,INF=2e9+10,P=1e9+7;

int n,m;
int rt,fa[N],son[N][2],sz[N];
ll sum[N],val[N],t[N];

void Show(){
	puts("Now Show The Tree");
	cout<<"root="<<rt<<endl;
	rep(i,1,n) {
		if(son[i][0]) cout<<i<<' '<<son[i][0]<<endl;
		if(son[i][1]) cout<<i<<' '<<son[i][1]<<endl;
	}
	rep(i,1,n) cout<<fa[i]<<' '<<val[i]<<' '<<sum[i]<<' '<<t[i]<<' '<<sz[i]<<endl;
}


void Up(int u){
	if(!u) return;
	sum[u]=val[u]+sum[son[u][1]]+sum[son[u][0]];
	sz[u]=sz[son[u][0]]+sz[son[u][1]]+1;
}

void Down(int u){
	if(!u||!t[u]) return;
	t[son[u][0]]+=t[u];
	t[son[u][1]]+=t[u];
	sum[son[u][0]]+=t[u]*sz[son[u][0]];
	sum[son[u][1]]+=t[u]*sz[son[u][1]];
	val[son[u][0]]+=t[u];
	val[son[u][1]]+=t[u];
	t[u]=0;
}


void rotate(int u) {
	int f=fa[u],ff=fa[f],d=(son[f][1]==u),df=(son[ff][1]==f);
	son[ff][df]=u,fa[u]=ff;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	son[u][!d]=f,fa[f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	if(!u) return;
	Down(u);
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if((son[f][1]==u)^(son[ff][1]==f)) rotate(f);
			else rotate(u);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

int Build(int l,int r){
	if(l>r) return 0;
	int u=(l+r)>>1;
	fa[son[u][0]=Build(l,u-1)]=u;
	fa[son[u][1]=Build(u+1,r)]=u;
	Up(u);
	return u;
}

char opt[10]; 

void DownNode(int rt,int x){
	int u=rt;
	while(u!=x) {
		Down(u);
		u=son[u][x>u];
	}
}


ll Que(int l,int r){
	if(l==1&&r==n) return sum[rt];
	if(l>1) {
		DownNode(rt,l-1);
		Splay(l-1,0);
	}
	if(r<n) {
		DownNode(rt,r+1),Splay(r+1,l-1);
		return sum[son[r+1][0]];
	}
	return sum[son[rt][1]];
}


void Upd(int l,int r,int x){
	if(l==1&&r==n) {
		sum[rt]+=1ll*x*sz[rt];
		t[rt]+=x;
		val[rt]+=x;
		return;
	}
	if(l>1) {
		DownNode(rt,l-1);
		Splay(l-1,0);
	}
	if(r<n) {
		DownNode(rt,r+1),Splay(r+1,l-1);
		sum[son[r+1][0]]+=x*sz[son[r+1][0]];
		val[son[r+1][0]]+=x;
		t[son[r+1][0]]+=x;
		Splay(son[r+1][0],0);
		return;
	}
	sum[son[rt][1]]+=sz[son[rt][1]];
	t[son[rt][1]]+=x;
	val[son[rt][1]]+=x;
	Splay(son[rt][1],0);
}




int main(){
	n=rd(),m=rd();
	rep(i,1,n) val[i]=rd();
	rt=Build(1,n);
	rep(i,1,m) {
		scanf("%s",opt);
		int l=rd(),r=rd();
		if(opt[0]=='Q') printf("%lld\n",Que(l,r));
		else Upd(l,r,rd());
	}
}

Splay.png

虽然打得很low但是还是能感觉到两种数据结构的速度差异。。。

其实写到后面也就是一些奇怪的操作的实现罢了,接下来我都是提供一种写法,仅供参考

T5 Robotic Sort

每次找到序列中最小的两个点,然后将一个较小的节点权值赋成无穷大(其实是将上一次完成排序的点删除),将两个点之间的区间翻转

#include<cstdio>
#include<algorithm>
#include<cctype>
#include<iostream>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

#define dir(x) (son[fa[x]][1]==x)


char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e5+10,INF=2e9+10,P=1e9+7;

bool be;

int n,m;
int rt,fa[N],son[N][2];
int num[N],t[N];

struct Node{
	int x,id;
	bool operator < (const Node __) const{
		return x<__.x||(id<__.id&&x==__.x);
	}
	bool operator == (const Node __) const{
		return x==__.x&&id==__.id;
	}
};

Node s[N],a[N];
int sz[N];

void Up(int u){
	if(!u) return;
	s[u]=a[u];
	sz[u]=1;
	if(son[u][0]) s[u]=min(s[u],s[son[u][0]]),sz[u]+=sz[son[u][0]];
	if(son[u][1]) s[u]=min(s[u],s[son[u][1]]),sz[u]+=sz[son[u][1]];
}

void Down(int u){
	if(!u||!t[u]) return;
	t[son[u][0]]^=1,t[son[u][1]]^=1;
	swap(son[u][0],son[u][1]);
	t[u]=0;
}

void rotate(int u) {
	int f=fa[u],ff=fa[f],d=dir(u);
	if(ff) son[ff][dir(f)]=u;
	fa[u]=ff;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	son[u][!d]=f,fa[f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	if(!u) return;
	Down(u);
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(u)^dir(f)) rotate(f);
			else rotate(u);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

int Build(int l,int r){
	if(l>r) return 0;
	int u=(l+r)>>1;
	t[u]=0;
	fa[son[u][0]=Build(l,u-1)]=u;
	fa[son[u][1]=Build(u+1,r)]=u;
	Up(u);
	return u;
}




int fir;
int Work(){
	int u=rt,res=0,l;
	if(fir) {
		while(1) {
			Down(u);
			if(son[u][0] && s[son[u][0]]==s[u]) {
				u=son[u][0];
				continue;
			}
			if(a[u]==s[u]) break;
			u=son[u][1];
		}
		s[u]=a[u]=(Node){(int)1e9,u};
		Up(u);
		Splay(u,0);
		l=u;
	} else fir=1,l=0;


	u=rt;
	while(1) {
		Down(u);
		if(son[u][0] && s[son[u][0]]==s[u]) {
			u=son[u][0];
			continue;
		}
		if(a[u]==s[u]) {
			res+=sz[son[u][0]];
			break;
		}
		res+=sz[son[u][0]]+1;
		u=son[u][1];
	}
	Splay(u,0);
	Down(u);
	if(son[u][1]) {
		u=son[u][1];
		while(1) {
			Down(u);
			if(son[u][0]) u=son[u][0];
			else break;
		}
		if(l) Splay(l,0);
		Splay(u,l);
		t[son[u][0]]^=1;
	} else {
		if(l) {
			Splay(l,0);
			t[son[rt][1]]^=1;
		} else t[rt]^=1;
	}
	return res+1;
}

bool ed;

int main(){
	while(~scanf("%d",&n) && n) {
		rep(i,1,n) a[i]=(Node){rd(),i};
		fa[rt=Build(1,n)]=0;
		fa[0]=0,sz[0]=0;
		s[0]=(Node){(int)1e9,0};
		fir=0;
		rep(i,1,n-1) printf("%d ",Work());
		printf("%d\n",n);
	}
}


\[\ \]

\[\ \]

T6 Queue-jumpers

这题涉及到了多种\(Splay\)经典操作

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=2e5+10;



#define dir(x) (son[fa[x]][1]==x)

int n,m,rt;
int h[N],cnt,c;
int L[N],R[N];
int fa[N],son[N][2],sz[N];

void Show(){
	rep(i,1,c) {
		if(son[i][0]) cout<<i<<' '<<son[i][0]<<" 0"<<endl;
		if(son[i][1]) cout<<i<<' '<<son[i][1]<<" 1"<<endl;
	}
	rep(i,1,c) cout<<fa[i]<<' '<<sz[i]<<endl;
}

void Up(int u) {
	if(!u) return;
	sz[u]=R[u]-L[u]+1;
	if(son[u][0]) sz[u]+=sz[son[u][0]];
	if(son[u][1]) sz[u]+=sz[son[u][1]];
}

void rotate(int u) {
	int f=fa[u],ff=fa[f],d=dir(u);
	if(ff) son[ff][dir(f)]=u; fa[u]=ff; 
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	son[u][!d]=f,fa[f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	if(!u) return;
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

int Build(int l,int r){
	if(l>r) return 0;
	int u=(l+r)>>1;
	fa[son[u][0]=Build(l,u-1)]=u;
	fa[son[u][1]=Build(u+1,r)]=u;
	Up(u);
	return u;
}

int opt[N],optx[N],id[N];
char option[10];

void Top(int x){
	Splay(x,0);
	if(!son[x][0]) return;
	if(!son[x][1]){
		swap(son[x][0],son[x][1]);
		return;
	} else {
		int u=son[x][1];
		while(son[u][0]) u=son[u][0];
		Splay(u,x);
		fa[son[x][0]]=son[x][1];
		son[son[x][1]][0]=son[x][0];
		Up(son[x][1]);
		son[x][0]=0;
	}
}

int Que(int x){
	Splay(x,0);
	return sz[son[x][0]]+1;
}

int Rank(int x){
	int u=rt;
	while(u) {
		if(sz[son[u][0]]>=x) {
			u=son[u][0];
			continue;
		}
		x-=sz[son[u][0]];
		if(R[u]-L[u]+1>=x) {
			Splay(u,0);
			return L[u]+x-1;
		}
		x-=R[u]-L[u]+1;
		u=son[u][1];
	}
	return -1;
}



int main(){
	rep(kase,1,rd()) {
		n=rd(),m=rd();
		cnt=0;
		rep(i,1,m) {
			scanf("%s",option);
			optx[i]=rd();
			if(option[0]=='T') opt[i]=0,h[++cnt]=optx[i];
			else if(option[0]=='Q') opt[i]=1,h[++cnt]=optx[i];
			else opt[i]=2;
		}
		sort(h+1,h+cnt+1);
		cnt=unique(h+1,h+cnt+1)-h-1;
		int pre=0;
		c=0;
		rep(i,1,cnt) {
			if(h[i]-1>pre) L[++c]=pre+1,R[c]=h[i]-1;
			L[++c]=h[i],R[c]=h[i];
			id[i]=c;
			pre=h[i];
		}
		if(n>pre) L[++c]=pre+1,R[c]=n;
		fa[rt=Build(1,c)]=0;fa[0]=sz[0]=0;
		printf("Case %d:\n",kase);
		rep(i,1,m) {
			if(opt[i]==0) Top(id[lower_bound(h+1,h+cnt+1,optx[i])-h]);
			else if(opt[i]==1) printf("%d\n",Que(id[lower_bound(h+1,h+cnt+1,optx[i])-h]));
			else printf("%d\n",Rank(optx[i]));
		}
	}
}


\[\ \]

\[\ \]

T7 Play with Chain

对于移动链的操作,先把链断开,再找到对应插入位置,再插入

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=3e5+10;

#define dir(x) (son[fa[x]][1]==x)


int n,m,rt;
int fa[N],son[N][2],sz[N],t[N];

void Show(){
	puts("Now Show The Splay Tree");
	cout<<"rt="<<rt<<endl;
	rep(i,1,n) {
		if(son[i][0]) cout<<i<<' '<<son[i][0]<<" 0"<<endl;
		if(son[i][1]) cout<<i<<' '<<son[i][1]<<" 1"<<endl;
	}
	rep(i,1,n) cout<<son[i][0]<<' '<<son[i][1]<<' '<<"t="<<t[i]<<' '<<"sz="<<sz[i]<<endl;
}


void Up(int u) {
	if(!u) return;
	sz[u]=1;
	if(son[u][0]) sz[u]+=sz[son[u][0]];
	if(son[u][1]) sz[u]+=sz[son[u][1]];
}

void Down(int u){
	if(!u||!t[u]) return;
	swap(son[u][0],son[u][1]);
	t[son[u][0]]^=1;
	t[son[u][1]]^=1;
	t[u]=0;
}

void rotate(int u){
	int f=fa[u],ff=fa[f],d=dir(u);
	fa[u]=ff; if(ff) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[son[u][!d]=f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	if(!u) return;
	Down(u);
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}
int Find(int x){
	int u=rt;
	while(1) {
		Down(u);
		if(sz[son[u][0]]>=x) {
			u=son[u][0];
			continue;
		}
		x-=sz[son[u][0]];
		if(x==1) break;
		x--;
		u=son[u][1];
	}
	return u;
}


void Move(int l,int r,int c){
	if(l==1&&r==n) return;
	if(l==1) l=0;
	else {
		l--;
		l=Find(l);
		Splay(l,0);
	}
	int tmp;
	if(r==n) {
		tmp=son[rt][1];
		son[rt][1]=0;
		Up(rt);
	} else {
		r++;
		r=Find(r);
		Splay(r,l);
		tmp=son[r][0];
		son[r][0]=0;
		Up(r),Up(rt);
	}
	if(c==0) {
		Splay(Find(1),0);
		fa[son[rt][0]=tmp]=rt;
		Up(rt);
		return;
	}
	Splay(Find(c),0);
	if(son[rt][1]) {
		int u;
		Splay(u=Find(c+1),rt);
		son[u][0]=tmp;
		fa[tmp]=u;
		Up(u);
		Up(rt);
	} else {
		son[rt][1]=tmp;
		fa[tmp]=rt;
		Up(rt);
	}
}

void Rev(int l,int r){
	if(l==1&&r==n) {
		t[rt]^=1;
		return;
	}
	if(l==1) l=0;
	else {
		l--;
		l=Find(l);
		Splay(l,0);
	}
	if(r==n) t[son[rt][1]]^=1;
	else {
		r++;
		r=Find(r);
		Splay(r,l);
		t[son[r][0]]^=1;
	}
}

int printcnt;
void Getline(int x){
	Down(x);
	if(son[x][0]) Getline(son[x][0]);
	printf("%d%c",x,++printcnt==n?'\n':' ');
	if(son[x][1]) Getline(son[x][1]);
}

int Build(int l,int r){
	if(l>r) return 0;
	int u=(l+r)>>1;
	t[u]=0;
	fa[son[u][0]=Build(l,u-1)]=u;
	fa[son[u][1]=Build(u+1,r)]=u;
	Up(u);
	return u;
}
char opt[10];
int main(){
	while(~scanf("%d%d",&n,&m) && ~n ) {
		fa[rt=Build(1,n)]=0;
		fa[0]=sz[0]=0;
		rep(i,1,m) {
			scanf("%s",opt);
			if(opt[0]=='C') {
				int a=rd(),b=rd(),c=rd();
				Move(a,b,c);
			} else {
				int l=rd(),r=rd();
				Rev(l,r);
			}
		}
		printcnt=0;
		Getline(rt);
	}
}

T8 文本编辑器editor0

没错一百万的数据

不过这题数据好像有锅

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1<<21;

#define dir(x) (son[fa[x]][1]==x)


int n,rt;
int fa[N],son[N][2],sz[N],t[N];
char s[N],val[N];



void Up(int u) {
	if(!u) return;
	sz[u]=1;
	if(son[u][0]) sz[u]+=sz[son[u][0]];
	if(son[u][1]) sz[u]+=sz[son[u][1]];
}
void Down(int u){
	if(!u||!t[u]) return;
	swap(son[u][0],son[u][1]);
	t[son[u][0]]^=1;
	t[son[u][1]]^=1;
	t[u]=0;
}

void Getline(int x){
	Down(x);
	if(son[x][0]) Getline(son[x][0]);
	putchar(val[x]);
	if(son[x][1]) Getline(son[x][1]);
}
void Show(){
	puts("Now Show The Splay Tree");
	rep(i,1,n) {
		if(son[i][0]) cout<<i<<' '<<son[i][0]<<" 0"<<endl;
		if(son[i][1]) cout<<i<<' '<<son[i][1]<<" 1"<<endl;
	}
	rep(i,1,n) cout<<fa[i]<<' '<<sz[i]<<' '<<t[i]<<' '<<val[i]<<endl;
}

void rotate(int u){
	int f=fa[u],ff=fa[f],d=dir(u);
	Down(ff),Down(f),Down(u);
	fa[u]=ff; if(ff) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[son[u][!d]=f]=u;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	if(!u) return;
	Down(u);
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

char opt[10];

int Build(int l,int r){
	if(l>r) return 0;
	int u=++n,mid=(l+r)>>1;
	val[u]=s[mid];
	fa[son[u][0]=Build(l,mid-1)]=u;
	fa[son[u][1]=Build(mid+1,r)]=u;
	Up(u);
	return u;
}

int Find(int x){ 
	int u=rt;
	while(1) {
		Down(u);
		if(sz[son[u][0]]>=x) {
			u=son[u][0];
			continue;
		}
		x-=sz[son[u][0]];
		if(x==1) break;
		x--;
		u=son[u][1];
	}
	return u;
}

int Next(int x,int d){
	Splay(x,0);
	Down(x);
	x=son[x][d];
	Down(x);
	while(son[x][!d]){
		x=son[x][!d];
		Down(x);
	}
	return x;
}


int now;
int main(){
	rd();
	now=rt=n=1;
	while(~scanf("%s",opt)) {
		if(opt[0]=='I') {
			int c=0,l=rd();
			rep(i,1,l) s[++c]=getchar();
			int t=Build(1,c);
			int nxt=Next(now,1);
			if(nxt) {
				Splay(nxt,0);
				Splay(now,rt);
			}
			fa[son[now][1]=t]=now;
			Up(now),Up(rt);
		} else if(opt[0]=='M') now=Find(rd()+1);
		else if(opt[0]=='G') {
			int nxt=Next(now,1);
			putchar(val[nxt]);
			if(val[nxt]!='\n') puts("");
		} else if(opt[0]=='N') now=Next(now,1);
		else if(opt[0]=='P') now=Next(now,0);
		else if(opt[0]=='D') {
			Splay(now,0);
			int l=rd();
			if(sz[son[now][1]]==l) {
				son[now][1]=0;
				Up(now);
				continue;
			}
			int t=Find(sz[son[now][0]]+l+2);
			Splay(t,now);
			son[t][0]=0;
			Up(t),Up(now);
		} else {
			Splay(now,0);
			int l=rd();
			if(sz[son[now][1]]==l) {
				t[son[now][1]]^=1;
				continue;
			}
			int t=Find(sz[son[now][0]]+l+2);
			Splay(t,now);
			::t[son[t][0]]^=1;
		}
	}
}





\[\ \]

\[\ \]

T9 维修数列

不多说了,注意代码常数,如果你写T了,可以看一下我的实现

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cassert>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=5e5+10,INF=1e9+10;

#define dir(x) (son[fa[x]][1]==x)

int m,rt;
int fa[N],son[N][2];
int Setmark[N],Revmark[N];
int stk[N],top;



struct Node{
	ll ls,rs,s,ma;
	int sz;
	Node operator + (const Node x) const {
		Node res;
		res.ls=max(ls,s+x.ls);
		res.rs=max(x.rs,x.s+rs);
		res.s=s+x.s;
		res.ma=max(max(ma,x.ma),rs+x.ls);
		res.sz=sz+x.sz;
		return res;
	}
	void operator = (const int x) {
		s=sz*x;
		ls=rs=ma=max(x,sz*x);
	}
}s[N],val[N];


void Up(int u){
	if(!u) return;
	if(son[u][0]) s[u]=s[son[u][0]]+val[u];
	else s[u]=val[u];
	if(son[u][1]) s[u]=s[u]+s[son[u][1]];
}

void Down(int u){
	if(!u) return;
	if(Setmark[u]!=INF) {
		if(son[u][0]) {
			Setmark[son[u][0]]=Setmark[u];
			Revmark[son[u][0]]=0;
			s[son[u][0]]=Setmark[u];
			val[son[u][0]]=Setmark[u];
		}
		if(son[u][1]) {
			Setmark[son[u][1]]=Setmark[u];
			Revmark[son[u][0]]=0;
			s[son[u][1]]=Setmark[u];
			val[son[u][1]]=Setmark[u];
		}
		Setmark[u]=INF;
	}
	if(Revmark[u]) {
		if(son[u][0]) {
			Revmark[son[u][0]]^=1;
			swap(s[son[u][0]].ls,s[son[u][0]].rs);
		}
		if(son[u][1]) {
			Revmark[son[u][1]]^=1;
			swap(s[son[u][1]].ls,s[son[u][1]].rs);
		}
		swap(son[u][0],son[u][1]);
		Up(u);
		Revmark[u]=0;
	}
}

void rotate(int u){
	int f=fa[u],ff=fa[f],d=dir(u);
	Down(ff),Down(f),Down(u);
	fa[u]=ff; if(ff) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	fa[f]=u,son[u][!d]=f;
	Up(f),Up(u),Up(ff);
}

void Splay(int u,int to){
	Down(u);
	while(fa[u]!=to) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(u)^dir(f)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
	if(!to) rt=u;
}

int a[N],tot;
int Build(int l,int r){
	if(l>r) return 0;
	int mid=(l+r)>>1,u=stk[top--];
	val[u].sz=1;val[u]=a[mid];
	Setmark[u]=INF;Revmark[u]=0;
	fa[son[u][0]=Build(l,mid-1)]=u;
	fa[son[u][1]=Build(mid+1,r)]=u;
	Up(u);
	return u;
}

int Find(int x){ 
	int u=rt;
	while(1) {
		Down(u);
		if(s[son[u][0]].sz>=x) { u=son[u][0]; continue; }
		if((x-=s[son[u][0]].sz)==1) break;
		x--,u=son[u][1];
	}
	return u;
}

void Insert(int p){
	p++;
	Splay(Find(p),0);
	Splay(Find(p+1),rt);
	fa[son[son[rt][1]][0]=Build(1,tot)]=son[rt][1];
	Up(son[rt][1]),Up(rt);
}

queue <int> que;
void Del(int l,int r){
	r+=2;
	Splay(Find(l),0);
	Splay(Find(r),rt);
	que.push(son[son[rt][1]][0]);
	while(!que.empty()) {
		int u=que.front(); que.pop();
		stk[++top]=u;
		if(son[u][0]) que.push(son[u][0]);
		if(son[u][1]) que.push(son[u][1]);
	}
	son[son[rt][1]][0]=0;
	Up(son[rt][1]),Up(rt);
}

void Set(int l,int r,int x){
	r+=2;
	Splay(Find(l),0);
	Splay(Find(r),rt);
	int t=son[son[rt][1]][0];
	Revmark[t]=0,Setmark[t]=x;
	s[t]=x,val[t]=x;
	Up(son[rt][1]),Up(rt);
}

void Reverse(int l,int r){
	r+=2;
	Splay(Find(l),0);
	Splay(Find(r),rt);
	int t=son[son[rt][1]][0];
	if(Setmark[t]!=INF) return;
	Revmark[t]^=1;
	swap(s[t].ls,s[t].rs);
	Up(son[rt][1]),Up(rt);
}

ll GetSum(int l,int r){
	r+=2;
	Splay(Find(l),0);
	Splay(Find(r),rt);
	return s[son[son[rt][1]][0]].s;
}

ll GetAns(){
	return s[rt].ma;
}
char opt[20];


int main(){ 
	tot=rd(),m=rd();
	drep(i,N-1,1) stk[++top]=i;
	tot+=2;
	rep(i,2,tot-1) a[i]=rd();
	a[tot]=a[1]=-INF;
	fa[rt=Build(1,tot)]=0;
	rep(tttt,1,m) {
		scanf("%s",opt);
		if(opt[0]=='I') {
			int p=rd();
			rep(i,1,tot=rd()) a[i]=rd();
			Insert(p);
		} else if(opt[0]=='D') {
			int l=rd(),r=rd()+l-1;
			Del(l,r);
		} else if(opt[0]=='M'&&opt[2]=='K') {
			int l=rd(),r=rd()+l-1;
			Set(l,r,rd());
		} else if(opt[0]=='R') {
			int l=rd(),r=rd()+l-1;
			Reverse(l,r);
		} else if(opt[0]=='G') {
			int l=rd(),r=rd()+l-1;
			printf("%lld\n",GetSum(l,r));
		} else printf("%lld\n",GetAns());
	}
}

\[\ \]

\[\ \]

T10 Box

毕竟是压轴的题,还是有一定思维难度的

(其实就是一个LCT裸题嘛)

做法是,将每棵树化成括号序列,建立\(Splay\)森林

一个子树就是一段区间,然后就可以直接整个区间移动了

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cassert>
#include<cstring>
using namespace std;

#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)

char IO;
int rd(){
	int s=0,f=0;
	while(!isdigit(IO=getchar())) if(IO=='-') f=1;
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1e5+10,INF=1e9+10;

#define dir(x) (son[fa[x]][1]==x)

bool be;

int n,m;
int fa[N],son[N][2];

struct Edge{
	int to,nxt;
}e[N<<1];
int head[N],ecnt,ind[N];
void AddEdge(int u,int v){
	e[++ecnt]=(Edge){v,head[u]};
	head[u]=ecnt;
	ind[v]++;
}

int line[N],lc;
void dfs(int u){
	line[++lc]=u;
	for(int i=head[u];i;i=e[i].nxt) {
		int v=e[i].to;
		dfs(v);
	}
	line[++lc]=u+n;
}
void rotate(int u){
	int f=fa[u],ff=fa[f],d=dir(u);
	fa[u]=ff; if(ff) son[ff][dir(f)]=u;
	son[f][d]=son[u][!d]; if(son[u][!d]) fa[son[u][!d]]=f;
	son[u][!d]=f,fa[f]=u;
}
void Splay(int u,int to){
	while(fa[u]!=to && fa[u]) {
		int f=fa[u],ff=fa[f];
		if(ff!=to) {
			if(dir(f)^dir(u)) rotate(u);
			else rotate(f);
		}
		rotate(u);
	}
}
int Build(int l,int r){
	if(l>r) return 0;
	int mid=(l+r)>>1,u=line[mid];
	fa[son[u][0]=Build(l,mid-1)]=u;
	fa[son[u][1]=Build(mid+1,r)]=u;
	return u;
}

int GetRoot(int x){
	Splay(x,0);
	while(son[x][0]) x=son[x][0];
	Splay(x,0);
	return x;
}

void Move(int x,int to){
	Splay(x,0);
	if(son[x][0]) {
		int l=son[x][0];
		while(son[l][1]) l=son[l][1];
		Splay(l,0);
		Splay(x+n,l);
		int r=x+n;
		r=son[r][1];
		while(son[r][0]) r=son[r][0];
		Splay(r,l);
		if(!to) {
			fa[son[r][0]]=0;
			son[r][0]=0;
			return ;
		}
		x=son[r][0],son[r][0]=0;
		fa[x]=0;
		Splay(to,0);
		if(fa[x]) {
			Splay(x,0);
			son[r][0]=x;fa[x]=r;
			return;
		}
		int t=son[to][1];
		while(son[t][0]) t=son[t][0];
		Splay(t,to);
		son[t][0]=x;
		fa[x]=t;
	} else {// A whole tree
		if(!to) return;
		Splay(to,0);
		if(fa[x]) return;
		int t=son[to][1];
		while(son[t][0]) t=son[t][0];
		Splay(t,to);
		son[t][0]=x;
		fa[x]=t;
	}
}

bool ed;
int fir;
char opt[10];
int main(){
	//cout<<&ed-&be<<endl;
	while(~scanf("%d",&n)) {
		if(fir) puts("");
		else fir=1;
		memset(ind,0,sizeof ind);memset(head,0,sizeof head),ecnt=0;
		lc=0;
		rep(i,1,n) {
			int x=rd();
			if(x) AddEdge(x,i);
		}
		rep(i,1,n) if(!ind[i]) {
			int t=lc+1;
			dfs(i);
			fa[Build(t,lc)]=0;
		}
		fa[0]=0;
		rep(i,1,m=rd()) {
			scanf("%s",opt);
			if(opt[0]=='Q') printf("%d\n",GetRoot(rd()));
			else {
				int x=rd(),to=rd();
				if(x^to) Move(x,to);
			}
		}
	}
}

posted @ 2019-09-26 10:55  chasedeath  阅读(245)  评论(1编辑  收藏  举报