指针版Splay
指针版Splay
定义
Splay 树, 或 伸展树,是一种平衡二叉查找树,它通过 Splay/伸展操作 不断将某个节点旋转到根节点,使得整棵树仍然满足二叉查找树的性质,能够在均摊
一些变量
struct edge{
edge *f,*son[2];
int size,cnt,num;
bool is;
edge(edge *A,edge *B,edge *C,int D,int E,int F){
f=A,son[0]=B,son[1]=C,size=D,cnt=E,num=F,is=true;
}
};
一些基础的操作
recalc
重新计算该节点的size(首先得保证这不是节点emp)
void recalc(edge *now){
if(now->is)
now->size=now->son[0]->size+now->son[1]->size+now->cnt;
}
get
返回
bool get(edge *now){
return (now==now->f->son[1]);
}
connect
将节点
void connect(edge *x,edge *y,int whi){
x->f=y;
y->son[whi]=x;
recalc(x);
recalc(y);
return ;
}
Splay操作
操作Splay即为把一个点旋转到根节点,因此我们需要通过旋转将其旋上去
Splay中最重要便是旋转,不同于一般的平衡树使用单旋,Splay采用双旋
具体而言,就是当遇到一字形的时候,我们先旋转它的父亲,再旋转它自己
否则的话我们连续旋转两次它自己
最后新的根就是它自己
而有的时候,我们可能会想把一个节点旋转到根节点的下面,此时我们就要特殊判断
代码
void splay(edge *now,edge *dist = emp){
for(edge *fa;fa=now->f,fa->is&&fa!=dist;rotate(now))
if(fa->f->is&&fa->f!=dist)rotate(get(now)==get(fa)?fa:now);
if(!dist->is)rt=now;
}
这个函数意为我们将节点
我们可以选择传一个参数或者两个参数,如果只传一个参数的话
如果传两个参数的话一般而言
在循环中,我们先找出
接下来我们判断一下它父亲的父亲是不是目标节点或者空,如果是的话说明我们只需要再旋转一次就可以完成任务了
否则的话我们还要旋转两次以上,我们就判断一下是之字形还是一字形,分别旋转一次它自身或它的父亲
然后我们发现,无论是之字形还是一字形,第二次旋转我们都要旋转一次自身
因此我们可以利用for语句的特性,第一个分号之前是最开始执行的,第二个分号之前是每次执行循环语句前执行的,用来判断是否符合循环条件,第二个分号之后是每一次执行完循环语句后执行的
因此我们在第二个分号之前给fa->is&&fa!=dist
是判断循环是否结束
最后不要忘记对于旋转到根节点的
旋转操作
我们模拟一次普通的左旋,看一下是怎么旋转的
我们的目标是
那么我们首先将now->son[1]接到fa->son[0]
然后我们把now接到gr_fa的get(fa)上
最后我们将fa接到now->son[1]上
那么对于右旋,则是
那么我们首先将now->son[0]接到fa->son[1]
然后我们把now接到gr_fa的get(fa)上
最后我们将fa接到now->son[0]上
我们发现左旋和右旋的区别只在于是
因此我们提前判断
那么代码就呼之欲出了
注意:因为我们最后
代码
void rotate(edge *now){
edge *fa=now->f,*gr_fa=fa->f;
int whi=get(now);//0左1右
connect(now->son[1^whi],fa,0^whi);
connect(now,gr_fa,get(fa));
connect(fa,now,1^whi);
}
find操作
我们先通过二叉查找树的性质找到
注意到有时候我们不一定会找到该节点,此时我们就会返回该数的左边的节点或者右边的节点
代码
void find(int x){
if(!rt->is)return ;
edge *now=rt;
while(x!=now->num&&now->son[x>now->num]->is)
now=now->son[x>now->num];
splay(now);
}
before和after
首先我们先把节点
否则我们要判断一下这个数是不是已经是
代码
edge* before(int x){
find(x);
if(rt->num<x)return rt;
edge *now=rt->son[0];
while(now->son[1]->is)now=now->son[1];
splay(now);
return now;
}
edge* after(int x){
find(x);
if(rt->num>x)return rt;
edge *now=rt->son[1];
while(now->son[0]->is)now=now->son[0];
splay(now);
return now;
}
Insert操作
Insert比较常规,我们只需要注意我们插入完节点要
void Insert(int x){
edge *now=rt,*fa=emp;
while(now->is){
if(now->num==x){now->cnt++;splay(now);return ;}
fa=now;now=now->son[x>now->num];
}
now=new edge(fa,emp,emp,1,1,x);
connect(now,fa,x > fa->num);
splay(now);
return ;
}
Delete操作
删除操作比较特别,它有两种实现方式
第一种是我们将要删除的点的前驱旋转到根节点,将后继旋转到根节点的右子树,那么要删除的点就在后继的左子树上
第二种是我们把要删除的点旋转到根节点,如果删除后这个点一个数不剩,就将该节点的前驱找到并旋转到根,直接连接到原来要删除的点的右子树上
第一种的话就要在写有两个参数的Splay操作,第二种就不需要,但还是比较推荐第一种写法
对于第一种写法,该节点删除完就为NULL了,因此我们要将后继的左儿子设置为
注意:在这里,我们最后要更新节点
代码
void Delete(int x){//第一种
edge *tmp1=before(x);
edge *tmp2=after(x);
splay(tmp1);
splay(tmp2,tmp1);
edge *now=tmp2->son[0];
if(now->num!=x)return ;
if(now->cnt>1){
now->cnt--;
splay(now);
return ;
}
connect(emp,tmp2,0);
delete now;
recalc(tmp2);
recalc(tmp1);
return ;
}
void Delete(int x){//第二种
find(x);
if(rt->num!=x)return ;
if(rt->cnt>1){
rt->cnt--;
recalc(rt);
return ;
}
edge *tmp=rt;
rt=rt->son[0];
before(tmp->num);
connect(tmp->son[1],rt,1);
delete tmp;
recalc(rt->son[0]),recalc(rt);
return ;
}
一些比较重要的细节
众所周知,有一种十分烦人的指针,叫做空指针(NULL)。在树形数据结构中,我们常常会不可避免地访问到空指针(例如一个节点只有右儿子,我们却访问了它的左儿子)。通常的方式是凡是访问了指针,都判断一下。然而这样效率十分低下,大量分支的存在使得代码冗长,常数变大,不易调试,可读性降低等问题。
我们可以考虑新开一个节点*emp
来表示空指针。这样,即使我们遇到了一个为空的指针now->siz
(应当为0),也不用担心会出现RE or WA。
因此,对于指针emp,我们将它的父亲,两个儿子都设为emp,对于size,cnt等数据我们就令之为0,然而,为了将其与一般的节点区分,我们使用is,令is为false即可,这样我们就怎样都不会访问到NULL指针了
写代码时一定要注意节点的初始化
emp节点指向的size,cnt不应当在任何时候被修改。
有时候我们要查前驱和后继,如果对emp指针进行Splay感觉有不好,因此我们考虑引入两个哨兵
最开始的时候,rt指向的就是其中一个哨兵
至于它的值,分别取最大值和最小值,size,cnt都赋值为0,从而不影响答案的计算,
并且两个哨兵也算是一个节点,只是不影响答案,因此is为true
最后的最后,无论进行什么操作,到至少要Splay一次从而保证复杂度
P3369 【模板】普通平衡树
讲解都在上面了
代码
#include<bits/stdc++.h>
using namespace std;
struct edge{
edge *f,*son[2];
int size,cnt,num;
bool is;
edge(edge *A,edge *B,edge *C,int D,int E,int F){
f=A,son[0]=B,son[1]=C,size=D,cnt=E,num=F,is=true;
}
void print(){
cout<<num<<" "<<(son[0]?son[0]->num:0)<<" "<<(son[1]?son[1]->num:0)<<" "<<(f?f->num:0)<<" "<<size<<" "<<cnt<<endl;
}
};
edge *A,*B,*C,*D,*E,*emp;
void print(edge *now){
now->print();
if(now->son[0]->is)print(now->son[0]);
if(now->son[1]->is)print(now->son[1]);
return ;
}
edge *rt;
struct Tree{
void recalc(edge *now){
if(now->is)
now->size=now->son[0]->size+now->son[1]->size+now->cnt;
}
bool get(edge *now){
return (now==now->f->son[1]);
}
void connect(edge *x,edge *y,int whi){
x->f=y;
y->son[whi]=x;
recalc(x);
recalc(y);
return ;
}
void rotate(edge *now){
edge *fa=now->f,*gr_fa=fa->f;
int whi=get(now);//0左1右
connect(now->son[1^whi],fa,0^whi);
connect(now,gr_fa,get(fa));
connect(fa,now,1^whi);
}
void splay(edge *now,edge *dist = emp){
for(edge *fa;fa=now->f,fa->is&&fa!=dist;rotate(now))
if(fa->f->is&&fa->f!=dist)rotate(get(now)==get(fa)?fa:now);
if(!dist->is)rt=now;
}
void find(int x){
if(!rt->is)return ;
edge *now=rt;
while(x!=now->num&&now->son[x>now->num]->is)
now=now->son[x>now->num];
splay(now);
}
void Insert(int x){
edge *now=rt,*fa=emp;
while(now->is){
if(now->num==x){now->cnt++;splay(now);return ;}
fa=now;now=now->son[x>now->num];
}
now=new edge(fa,emp,emp,1,1,x);
connect(now,fa,x > fa->num);
splay(now);
return ;
}
void Delete(int x){
edge *tmp1=before(x);
edge *tmp2=after(x);
splay(tmp1);
splay(tmp2,tmp1);
edge *now=tmp2->son[0];
if(now->num!=x)return ;
if(now->cnt>1){
now->cnt--;
splay(now);
return ;
}
connect(emp,tmp2,0);
delete now;
recalc(tmp2);
recalc(tmp1);
return ;
}
int check_num_rank(int x){
find(x);
if(rt->num>=x)return rt->son[0]->size+1;
else return rt->son[0]->size+rt->cnt+1;
}
edge* before(int x){
find(x);
if(rt->num<x)return rt;
edge *now=rt->son[0];
while(now->son[1]->is)now=now->son[1];
splay(now);
return now;
}
edge* after(int x){
find(x);
if(rt->num>x)return rt;
edge *now=rt->son[1];
while(now->son[0]->is)now=now->son[0];
splay(now);
return now;
}
edge* check_rank_num(int x){
edge *now=rt;
while(1){
if(now->son[0]->is&&x<=now->son[0]->size)now=now->son[0];
else if(now->son[1]->is&&x>now->son[0]->size+now->cnt)x-=now->son[0]->size+now->cnt,now=now->son[1];
else {
splay(now);
return now;
}
}
}
}T;
void init(){
emp=new edge(NULL,NULL,NULL,0,0,0);
emp->f=emp,emp->son[0]=emp,emp->son[1]=emp;
emp->is=false;
rt=new edge(emp,emp,emp,0,0,-1e8);
edge *tmp=new edge(emp,emp,emp,0,0,1e8);
T.connect(tmp,rt,1);
}
int main(){
int t,op,x;
init();
scanf("%d",&t);
while(t--){
scanf("%d%d",&op,&x);
switch(op){
case 1:T.Insert(x);break;
case 2:T.Delete(x);break;
case 3:printf("%d\n",T.check_num_rank(x));break;
case 4:printf("%d\n",T.check_rank_num(x)->num);break;
case 5:printf("%d\n",T.before(x)->num);break;
case 6:printf("%d\n",T.after(x)->num);break;
}
}
return 0;
}
P6136 【模板】普通平衡树(数据加强版)
代码
#include<bits/stdc++.h>
using namespace std;
struct edge{
edge *f,*son[2];
int size,cnt,num;
bool is;
int son_size(int whi){return son[whi]?son[whi]->size:0; }
edge(edge *A,edge *B,edge *C,int D,int E,int F){
f=A,son[0]=B,son[1]=C,size=D,cnt=E,num=F,is=true;
}
void print(){
cout<<num<<" "<<(son[0]?son[0]->num:0)<<" "<<(son[1]?son[1]->num:0)<<" "<<(f?f->num:0)<<" "<<size<<" "<<cnt<<endl;
}
};
edge *A,*B,*C,*D,*E,*emp;
void print(edge *now){
now->print();
if(now->son[0]->is)print(now->son[0]);
if(now->son[1]->is)print(now->son[1]);
return ;
}
edge *rt;
struct Tree{
void recalc(edge *now){
if(now->is)
now->size=now->son[0]->size+now->son[1]->size+now->cnt;
}
bool get(edge *now){
return (now==now->f->son[1]);
}
void connect(edge *x,edge *y,int whi){
x->f=y;
y->son[whi]=x;
recalc(x);
recalc(y);
return ;
}
void rotate(edge *now){
edge *fa=now->f,*gr_fa=fa->f;
int whi=get(now);//0左1右
connect(now->son[1^whi],fa,0^whi);
connect(now,gr_fa,get(fa));
connect(fa,now,1^whi);
}
void splay(edge *now,edge *dist = emp){
for(edge *fa;fa=now->f,fa->is&&fa!=dist;rotate(now))
if(fa->f->is&&fa->f!=dist)rotate(get(now)==get(fa)?fa:now);
if(!dist->is)rt=now;
}
void find(int x){
if(!rt->is)return ;
edge *now=rt;
while(x!=now->num&&now->son[x>now->num]->is)
now=now->son[x>now->num];
splay(now);
}
void Insert(int x){
edge *now=rt,*fa=emp;
while(now->is){
if(now->num==x){now->cnt++;splay(now);return ;}
fa=now;now=now->son[x>now->num];
}
now=new edge(fa,emp,emp,1,1,x);
connect(now,fa,x > fa->num);
splay(now);
return ;
}
void Delete(int x){
edge *tmp1=before(x);
edge *tmp2=after(x);
splay(tmp1);
splay(tmp2,tmp1);
edge *now=tmp2->son[0];
if(now->num!=x)return ;
if(now->cnt>1){
now->cnt--;
splay(now);
return ;
}
connect(emp,tmp2,0);
delete now;
recalc(tmp2);
recalc(tmp1);
return ;
}
int check_num_rank(int x){
find(x);
if(rt->num>=x)return rt->son[0]->size+1;
else return rt->son[0]->size+rt->cnt+1;
}
edge* before(int x){
find(x);
if(rt->num<x)return rt;
edge *now=rt->son[0];
while(now->son[1]->is)now=now->son[1];
splay(now);
return now;
}
edge* after(int x){
find(x);
if(rt->num>x)return rt;
edge *now=rt->son[1];
while(now->son[0]->is)now=now->son[0];
splay(now);
return now;
}
edge* check_rank_num(int x){
edge *now=rt;
while(1){
if(now->son[0]->is&&x<=now->son[0]->size)now=now->son[0];
else if(now->son[1]->is&&x>now->son[0]->size+now->cnt)x-=now->son[0]->size+now->cnt,now=now->son[1];
else {
splay(now);
return now;
}
}
}
}T;
void init(){
emp=new edge(NULL,NULL,NULL,0,0,0);
emp->f=emp,emp->son[0]=emp,emp->son[1]=emp;
emp->is=false;
rt=new edge(emp,emp,emp,0,0,-1);
edge *tmp=new edge(emp,emp,emp,0,0,(1<<30));
T.connect(tmp,rt,1);
}
int main(){
int n,m,t,op,x,last=0,ans=0;
init();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&x);
T.Insert(x);
}
while(m--){
scanf("%d%d",&op,&x);
switch(op){
case 1:T.Insert(x^last);break;
case 2:T.Delete(x^last);break;
case 3:last=T.check_num_rank(x^last),ans^=last;break;
case 4:last=T.check_rank_num(x^last)->num,ans^=last;break;
case 5:last=T.before(x^last)->num,ans^=last;break;
case 6:last=T.after(x^last)->num,ans^=last;break;
}
}
cout<<ans;
return 0;
}
P3391 【模板】文艺平衡树
Splay存在的价值在于维护区间,它无论怎么变换它的中序遍历不会改变
对于这题,对于一个需要翻转的区间
对于一个区间,我们不需要真的翻转,我们引入lazy_tag,给当前节点打上一个标记
不过要注意的是如果我们遇到一个lazy_tag,我们就要下放标记
仔细看代码,Splay操作会改变树的结构,但是我们没有改变Splay的写法,那会不会我们还没有下放标记我们就Splay上去了?
实际上不会,在每一次的打标记之前,我们会先找到l-1和r+1,在找l-1的时候,沿路上的标记都会被下放,因此当l-1节点Splay上来的时候沿路标记均已下放,没有影响,r+1也是同理
到了最后,我们只是修改了一下代表区间[l,r]的子树的根节点的lazy_tag便结束,因此没有影响
代码
#include<bits/stdc++.h>
using namespace std;
int read(){
int x=0,f=1;
char c=getchar();
while(c<'0'||'9'<c)f=(c=='-'?-1:1),c=getchar();
while('0'<=c&&c<='9')x=x*10+c-'0',c=getchar();
return x*f;
}
struct edge{
edge *f,*son[2];
int size,cnt,num,tag;
bool is;
edge(edge *A,edge *B,edge *C,int D,int E,int F){
f=A,son[0]=B,son[1]=C,size=D,cnt=E,num=F;
is=true;
tag=0;
}
void print(){
cout<<num<<" "<<tag<<" "<<son[0]->num<<" "<<son[1]->num<<" "<<f->num<<" "<<size<<" "<<cnt<<endl;
}
};
int n,m,l,r;
edge *emp,*rt;
struct Tree{
void recalc(edge *now){
if(now->is)
now->size=now->son[0]->size+now->son[1]->size+now->cnt;
}
int get(edge *now){
return now==now->f->son[1];
}
void pushdown(edge *now){
if(!now->is||!now->tag)return ;
now->son[0]->tag^=1;
now->son[1]->tag^=1;
swap(now->son[0],now->son[1]);
now->tag=0;
}
void connect(edge *x,edge *y,int whi){
x->f=y;
y->son[whi]=x;
recalc(x);
recalc(y);
}
void rotate(edge *now){
edge *fa=now->f,*gr_fa=fa->f;
int whi=get(now);
connect(now->son[1^whi],fa,0^whi);
connect(now,gr_fa,get(fa));
connect(fa,now,1^whi);
recalc(fa);
recalc(now);
}
void splay(edge *now,edge *dist=emp){
for(edge *fa;fa=now->f,fa->is&&fa!=dist;rotate(now))
if(fa->f->is&&fa->f!=dist)rotate(get(now)==get(fa)?fa:now);
if(!dist->is)rt=now;
}
edge* Kth(int x){
edge *now=rt;
while(1){
pushdown(now);
if(now->son[0]->is&&now->son[0]->size>=x)now=now->son[0];
else if(now->son[1]->is&&now->son[0]->size+now->cnt<x)x-=now->son[0]->size+now->cnt,now=now->son[1];
else {
splay(now);
return now;
}
}
}
void Insert(int x){
edge *now=rt,*fa=emp;
while(now->is){
fa=now;
now=now->son[x>now->num];
}
now=new edge(fa,emp,emp,1,1,x);
connect(now,fa,x>fa->num);
splay(now);
}
void rev(int l,int r){
edge *now=rt;
edge *tmp1=Kth(l-1);
edge *tmp2=Kth(r+1);
splay(tmp1);
splay(tmp2,tmp1);
tmp2->son[0]->tag^=1;
}
void print(edge *now){
if(!now->is)return ;
pushdown(now);
print(now->son[0]);
if(1<=now->num&&now->num<=n)
printf("%d ",now->num);
print(now->son[1]);
}
}T;
void init(){
emp=new edge(NULL,NULL,NULL,0,0,0);
emp->f=emp,emp->son[0]=emp,emp->son[1]=emp;
emp->is=false;
rt=new edge(emp,emp,emp,0,0,0);
edge *tmp1=new edge(emp,emp,emp,0,0,n+1);
T.connect(tmp1,rt,1);
}
int main(){
n=read(),m=read();
init();
for(int i=1;i<=n;i++)T.Insert(i);
while(m--){
l=read(),r=read();
T.rev(l,r);
}
T.print(rt);
return 0;
}
P4008 [NOI2003] 文本编辑器
这道题本身并不难,因为我们在操作的时候会多次改变树的结构,根节点也会改变
因此我们在操作之前要记住光标的位置,在操作结束后我们又Splay回光标的位置
那会不会我们操作后光标的位置的点就不见了呢
实际上不会,我们的根节点是光标无法操作的,它的右子树才是可能被操作的
如果我们给每一个字母都开一个节点,那未免太奢侈了,考虑其他办法
看题目,Insert操作不超过四千,我们可以尝试将录入的字符串放到一个数组里,我们只插入一个节点,在节点里面我们记录l和r,就是这个节点代表的字符串在数组中开始和结束的位置
那如果我们要找一个字符在一个节点的内部呢,记这个字符位置为x
我们可以将这一个节点分裂,因为我们的根节点是光标无法操作的,我们要找的点我们肯定需要操作(否则也不会找它)
因此我们分为
这样我们就可以大大减少节点的数量
对于Delete操作和Get操作,实际上都要遍历,只是要不要删除的区别
因此我们写在同一个函数里,Delete操作的话我们在遍历完后进行删除就可以了
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2*1024*1024+5;
char op[20],ch;
int Line[maxn],n;
struct edge{
edge *f,*son[2];
int size,len,num;
bool is;
int l,r;
edge(edge *A,edge *B,edge *C,int D,int E,int F,int G){
f=A,son[0]=B,son[1]=C;
size=D,len=E,is=true;
l=F,r=G;
num=0;
}
void print(){
cout<<l<<" "<<r<<" "<<num<<":";
for(int i=l;i<=r;i++)cout<<(char)Line[i];
cout<<endl;
}
};
edge *emp,*rt,*Link=rt;
struct My_Splay{
void recalc(edge *now){
if(now->is)
now->size=now->son[0]->size+now->son[1]->size+now->len;
}
int get(edge *now){
return (now==now->f->son[1]);
}
void connect(edge *x,edge *y,int whi){
x->f=y;
y->son[whi]=x;
recalc(x);
recalc(y);
}
void rotate(edge *now){
edge *fa=now->f,*gr_fa=fa->f;
int whi=get(now);
connect(now->son[1^whi],fa,0^whi);
connect(now,gr_fa,get(fa));
connect(fa,now,1^whi);
}
void Splay(edge *now,edge *dist = emp){
for(edge *fa;fa=now->f,fa->is&&fa!=dist;rotate(now))
if(fa->f->is&&fa->f!=dist)rotate(get(now)==get(fa)?fa:now);
if(!dist->is)rt=now;
}
void Insert(int L,int R){
edge *now=rt;
edge *tmp1=Next();
Splay(now);
Splay(tmp1,now);
edge *real_now=new edge(emp,emp,emp,R-L+1,R-L+1,L,R);
connect(real_now,tmp1,0);
recalc(tmp1),recalc(now);
}
void spilt(edge *now,int x){
edge *tmp1=new edge(emp,emp,emp,x,x,now->l,now->l+x-1);
edge *tmp2=new edge(emp,emp,emp,now->len-x,now->len-x,now->l+x,now->r);
connect(tmp2,tmp1,1);
connect(now->son[0],tmp1,0);
connect(now->son[1],tmp2,1);
connect(tmp1,now->f,get(now));
recalc(tmp2),recalc(tmp1);
rt=tmp1;
delete now;
}
edge* find(int x){
edge *now=rt;
while(now->is){
if(now->son[0]->is&&x<=now->son[0]->size)now=now->son[0];
else if(now->son[1]->is&&x>now->son[0]->size+now->len)x-=now->son[0]->size+now->len,now=now->son[1];
else break;
}
if(x>now->son[0]->size)
x-=now->son[0]->size;
Splay(now);
if(x!=0)
spilt(now,x);
return rt;
}
void Delete(int x,bool whi){
find(rt->son[0]->size+rt->len+x);
edge *tmp1=Next();
Splay(Link);
Splay(tmp1,Link);
real_print(tmp1->son[0],whi);
if(whi)connect(emp,tmp1,0),recalc(tmp1),recalc(Link);
}
void print(edge *now,bool whi){
//test
if(!now->is)return ;
if(!whi){
cout<<now->l<<" "<<now->r<<" "<<now->num<<":";
for(int i=now->l;i<=now->r;i++)cout<<(char)Line[i];
cout<<endl;
}print(now->son[0],whi);
print(now->son[1],whi);
if(whi)delete now;
return ;
}
void real_print(edge *now,bool whi){
//real
if(!now->is)return ;
real_print(now->son[0],whi);
if(!whi)
for(int i=now->l;i<=now->r;i++)printf("%c",Line[i]);
real_print(now->son[1],whi);
if(whi)delete now;
return ;
}
edge* Last(){
edge *now=rt;
now=now->son[0];
while(now->son[1]->is)now=now->son[1];
Splay(now);
return now;
}
edge* Next(){
edge *now=rt;
now=now->son[1];
while(now->son[0]->is)now=now->son[0];
Splay(now);
return now;
}
}T;
void init(){
emp=new edge(NULL,NULL,NULL,0,0,0,0);
emp->f=emp,emp->son[0]=emp,emp->son[1]=emp;
emp->is=false;
rt=new edge(emp,emp,emp,0,0,0,0);
rt->num=-1;
edge *tmp1=new edge(emp,emp,emp,0,0,0,0);
tmp1->num=-2;
T.connect(tmp1,rt,1);
}
int main(){
Line[0]='~';
init();
int t;
int cnt=0;
scanf("%d",&t);
while(t--){
scanf("%s",op);
if(op[0]=='M'){
scanf("%d",&n);
T.find(n);
T.Splay(rt);
Link=rt;
}
if(op[0]=='I'){
Link=rt;
scanf("%d",&n);
for(int i=1;i<=n;i++){
ch=getchar();
if(ch<32||ch>126)i--;
else Line[++cnt]=ch;
}
T.Insert(cnt-n+1,cnt);
T.Splay(Link);
}
if(op[0]=='D'){
Link=rt;
scanf("%d",&n);
T.Delete(n,true);
}
if(op[0]=='G'){
Link=rt;
scanf("%d",&n);
T.Delete(n,false);
cout<<endl;
T.Splay(Link);
}
if(op[0]=='P'){
T.find(Link->son[0]->size+Link->len-1);
Link=rt;
}
if(op[0]=='N'){
T.find(Link->son[0]->size+Link->len+1);
Link=rt;
}
}
return 0;
}
参考资料
数据结构学习笔记(1) Splay树 (splay实现区间操作
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具