题目描述
H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构。伸展树(splay)是一种数据
结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必修技能。有一天,邪恶的“卡”带着
他的邪恶的“常数”来企图毁灭 H 国。“卡”给 H 国的人洗脑说,splay 如果写成单旋的,将会更快。“卡”称
“单旋 splay”为“spaly”。虽说他说的很没道理,但还是有 H 国的人相信了,小 H 就是其中之一,spaly 马
上成为他的信仰。 而 H 国的国王,自然不允许这样的风气蔓延,国王构造了一组数据,数据由 m 个操作构成,
他知道这样的数据肯定打垮 spaly,但是国王还有很多很多其他的事情要做,所以统计每个操作所需要的实际代价
的任务就交给你啦。
数据中的操作分为五种:
1. 插入操作:向当前非空 spaly 中插入一个关键码为 key 的新孤立节点。插入方法为,先让 key 和根比较,如果
key 比根小,则往左子树走,否则往右子树走,如此反复,直到某个时刻,key 比当前子树根 x 小,而 x 的左子
树为空,那就让 key 成为 x 的左孩子; 或者 key 比当前子树根 x 大,而 x 的右子树为空,那就让 key 成为
x 的右孩子。该操作的代价为:插入后,key 的深度。特别地,若树为空,则直接让新节点成为一个单个节点的树
。(各节点关键码互不相等。对于“深度”的解释见末尾对 spaly 的描述)。
2. 单旋最小值:将 spaly 中关键码最小的元素 xmin 单旋到根。操作代价为:单旋前 xmin 的深度。
(对于单旋操作的解释见末尾对 spaly 的描述)。
3. 单旋最大值:将 spaly 中关键码最大的元素 xmax 单旋到根。操作代价为:单旋前 xmax 的深度。
4. 单旋删除最小值:先执行 2 号操作,然后把根删除。由于 2 号操作之后,根没有左子树,所以直接切断根和右子
树的联系即可(具体见样例解释)。 操作代价同 2 号操 作。
5. 单旋删除最大值:先执行 3 号操作,然后把根删除。 操作代价同 3 号操作。
对于不是 H 国的人,你可能需要了解一些 spaly 的知识,才能完成国王的任务:
a. spaly 是一棵二叉树,满足对于任意一个节点 x,它如果有左孩子 lx,那么 lx 的关键码小于 x 的关键码。
如果有右孩子 rx,那么 rx 的关键码大于 x 的关键码。
b. 一个节点在 spaly 的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。
c. 单旋操作是对于一棵树上的节点 x 来说的。一开始,设 f 为 x 在树上的父亲。如果 x 为 f 的左孩子,那么
执行 zig(x) 操作(如上图中,左边的树经过 zig(x) 变为了右边的树),否则执行 zag(x) 操作(在上图中,将
右边的树经过 zag(f) 就变成了左边的树)。每当执 行一次 zig(x) 或者 zag(x),x 的深度减小 1,如此反复,
直到 x 为根。总之,单旋 x 就是通过反复执行 zig 和 zag 将 x 变为根。
挖掘
我们来挖掘这些操作的性质。
插入,一定会插入在其前驱和后继中深度较大点的下方。
证明:
首先splay按中序遍历有序。
一个叶子的父亲一定是这个点的前驱或后继,这个更显然了你脑补或者反证都能得到。
那么找到前驱pre和后继next。插入该点前pre和next也互为前驱和后继,新点要么在pre下面要么在next下面。容易讨论,pre在next上面时,新点是next的左儿子,否则新点是pre的右儿子。
旋最小(旋最大同理),那么最小那个点一定没有左儿子,而且容易讨论它旋转到根后,树的形态只有很微小的改变(该点的父亲的左儿子变为该点右儿子,该点变成根且右儿子变成原来的根)
删除只有一个儿子的根就更简单了。
找前驱和后继可以用set实现,其余部分无脑LCT。
#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+10;
struct dong{
int x,y;
friend bool operator <(dong a,dong b){
return a.y<b.y||a.y==b.y&&a.x<b.x;
}
} zlt;
multiset<dong> s;
multiset<dong>::iterator it;
int tree[maxn][2],size[maxn],father[maxn],pp[maxn],sta[maxn],key[maxn];
bool bz[maxn];
int left[maxn],right[maxn];
int i,j,k,l,t,n,m,x,tot,top,ans,root;
bool czy,gjx;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int pd(int x){
return tree[father[x]][1]==x;
}
void update(int x){
size[x]=size[tree[x][0]]+size[tree[x][1]]+1;
}
void rotate(int x){
int y=father[x],z=pd(x);
father[x]=father[y];
if (father[y]) tree[father[y]][pd(y)]=x;
tree[y][z]=tree[x][1-z];
if (tree[x][1-z]) father[tree[x][1-z]]=y;
tree[x][1-z]=y;
father[y]=x;
if (pp[y]) pp[x]=pp[y],pp[y]=0;
update(y);
update(x);
}
void mark(int x){
if (!x) return;
bz[x]^=1;
swap(tree[x][0],tree[x][1]);
}
void down(int x){
if (bz[x]){
mark(tree[x][0]);
mark(tree[x][1]);
bz[x]=0;
}
}
void remove(int x,int y){
top=0;
while (x!=y){
sta[++top]=x;
x=father[x];
}
while (top) down(sta[top--]);
}
void splay(int x,int y){
remove(x,y);
while (father[x]!=y){
if (father[father[x]]!=y)
if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
rotate(x);
}
}
void access(int x){
int y,z;
splay(x,0);
z=tree[x][1];
if (z){
pp[z]=x;
father[z]=0;
}
tree[x][1]=0;
update(x);
while (1){
y=pp[x];
if (!y) break;
splay(y,0);
z=tree[y][1];
if (z){
pp[z]=y;
father[z]=0;
}
tree[y][1]=x;
father[x]=y;
pp[x]=0;
update(y);
splay(x,0);
}
}
void makeroot(int x){
access(x);
splay(x,0);
mark(x);
}
void link(int x,int y){
makeroot(y);
splay(y,0);
pp[y]=x;
}
void cut(int x,int y){
makeroot(x);
access(x);
splay(y,0);
pp[y]=0;
}
int getdep(int x){
makeroot(root);
access(x);
splay(x,0);
return size[tree[x][0]]+1;
}
int getfa(int x){
makeroot(root);
access(x);
splay(x,0);
int t=tree[x][0];
if (!t) return 0;
while (tree[t][1]){
down(t);
t=tree[t][1];
}
splay(t,0);
return t;
}
void splaymin(){
k=right[j];
l=getfa(j);
if (!l) return;
cut(j,l);
if (k) cut(j,k);
if (k) link(l,k);
left[l]=k;
link(j,root);
right[j]=root;
root=j;
}
void splaymax(){
k=left[j];
l=getfa(j);
if (!l) return;
cut(j,l);
if (k) cut(j,k);
if (k) link(l,k);
right[l]=k;
link(j,root);
left[j]=root;
root=j;
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top) putchar('0'+sta[top--]);
putchar('\n');
}
int main(){
freopen("splay.in","r",stdin);freopen("splay.out","w",stdout);
m=read();
while (m--){
t=read();
if (t==1){
x=read();
zlt.x=++tot;
zlt.y=x;
size[tot]=1;
key[tot]=x;
s.insert(zlt);
it=s.find(zlt);
if (it==s.begin()) czy=1;else czy=0;
if ((++it)==s.end()) gjx=1;else gjx=0;
it--;
if (czy&&gjx) root=tot;
else if (czy){
k=(*(++it)).x;
left[k]=tot;
link(tot,k);
}
else if (gjx){
k=(*(--it)).x;
right[k]=tot;
link(tot,k);
}
else{
j=(*(--it)).x;it++;k=(*(++it)).x;
if (getdep(j)<getdep(k)){
left[k]=tot;
link(tot,k);
}
else{
right[j]=tot;
link(tot,j);
}
}
ans=getdep(tot);
}
else if (t==2||t==4){
it=s.begin();
j=(*it).x;
ans=getdep(j);
splaymin();
if (t==4){
root=right[j];
if (root) cut(j,right[j]);
zlt.x=j;zlt.y=key[j];
s.erase(s.find(zlt));
}
}
else{
it=s.end();
it--;
j=(*it).x;
ans=getdep(j);
splaymax();
if (t==5){
root=left[j];
if (root) cut(j,left[j]);
zlt.x=j;zlt.y=key[j];
s.erase(s.find(zlt));
}
}
write(ans);
}
}