(笔记)Splay
二叉查找树定义
-
左子树所有节点权值小于根节点权值
-
右子树所有节点权值大于根节点权值
-
以左右子节点为根节点时子树同样满足上述条件
Splay(又称伸展树)
(二叉查找树)
伸展操作 \(Splay(x)\)
把一个节点拎到它所在 Splay 的根的操作。
- Zig(右旋)操作
令x为y的左儿子,则使y的左儿子指向x的右儿子,x的右儿子指向y。
- Zag(左旋)操作
令x为y的右儿子,则使y的右儿子指向x的左儿子,x的左儿子指向y。
上述操作互逆
根据实际情况所需进行zig、zag、zig-zig、zag-zag、zig-zag操作,组合即得到 Splay(x) 函数。
排名、查找前驱后继等函数
合理利用splay函数与二叉搜索树性质即可。
一些模板
普通平衡树
你会发现其实这个东西很好写,只要利用一些小技巧就可以不拆开 zig 和 zag,把它们都变成一个 rotate。为了改善平衡性,我们考虑当 \(u\) 往上(包括 \(u\))连续 2 个节点都在父亲的同一边,那么就先 rotate 父亲再 rotate 自己,否则直接 rotate 自己即可。
一些写指针的人真的是有病(指之前抄模板的我)所以打多几遍 LCT 就会获得一个好写很多的 Splay 模板如下:
const int N=3e5+5;
struct Node{int ch[2],sum,val,tg,fa;}t[N];
#define lc t[x].ch[0]
#define rc t[x].ch[1]
bool isRoot(int x){
int g=t[x].fa;
return t[g].ch[0]!=x&&t[g].ch[1]!=x;
}
void rotate(int x){
int y=t[x].fa;
int z=t[y].fa;
bool k=(t[y].ch[1]==x);
if(!isRoot(y))t[z].ch[t[z].ch[1]==y]=x;
t[x].fa=z;
t[y].ch[k]=t[x].ch[k^1];
if(t[x].ch[k^1])t[t[x].ch[k^1]].fa=y;
t[y].fa=x;
t[x].ch[k^1]=y;
pushup(y);
}
void splay(int x,int T){
int y,z;
while(!isRoot(x)){
y=t[x].fa,z=t[y].fa;
if(!isRoot(y))
((t[y].ch[1]==x)^(t[z].ch[1]==y))?rotate(x):(z==T?rotate(x):rotate(y));
rotate(x);
}
pushup(x);
}
void split(int x,int y){
splay(x);
splay(y);
}
然后笔者看了一下,发现针对 LCT 写的 Splay 确实有所欠缺,因为这样改善平衡性无法保证每次执行 Splay 操作原根一定是新根的儿子,但是 Splay 的很多操作都依赖于这个性质。于是乎就需要写一个Splay(x,T),\(T\) 表示原根即可。
代码贴贴:
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,INF=1e8;
int n,opt,x;
struct node{
int siz,val,cnt;
node *lson,*rson,*fa;
};
node *rt,nd[N],*null;
int gid;
node* NewNode(int val){
nd[++gid].val=val;
nd[gid].siz=1;
nd[gid].cnt=1;
nd[gid].lson=null;
nd[gid].rson=null;
return nd+gid;
}
void init(){
gid=0;
null=nd+gid;
node *q=NewNode(INF);
rt=NewNode(-INF);
rt->rson=q;
q->fa=rt;
rt->siz=2;
rt->fa=null;
}
void upd(node* x){
x->siz=x->cnt;
if(x->lson!=null)x->siz+=x->lson->siz;
if(x->rson!=null)x->siz+=x->rson->siz;
}
void zig(node *x){
node *y=x->fa,*z=y->fa;
y->lson=x->rson;
if(x->rson!=null)x->rson->fa=y;
upd(y);
x->rson=y;
y->fa=x;
upd(x);
x->fa=z;
if(z!=null){
if(z->lson==y)z->lson=x;
else z->rson=x;
}
}
void zag(node* x){
node *y=x->fa,*z=y->fa;
y->rson=x->lson;
if(x->lson!=null)x->lson->fa=y;
upd(y);
x->lson=y;
y->fa=x;
upd(x);
x->fa=z;
if(z!=null){
if(z->lson==y)z->lson=x;
else z->rson=x;
}
}
void splay(node *x,node *T){
while(x->fa!=T){
node *y=x->fa,*z=y->fa;
if(z==T){
if(x==y->lson)zig(x);
else zag(x);
}
else {
bool L=(x==y->lson),R=(y==z->lson);
if(L&&R)zig(y),zig(x);
else if((!L)&&(!R))zag(y),zag(x);
else if(L&&(!R))zig(x),zag(x);
else zag(x),zig(x);
}
}
if(x->fa==null)rt=x;
}
node* findK(node* now,int k){
int lsize=0;
if(now->lson!=null)lsize=now->lson->siz;
if(k<=lsize)return findK(now->lson,k);
else if(k<=lsize+now->cnt)return now;
else return findK(now->rson,k-lsize-now->cnt);
}
node* pred(node *now,int val,node* optimal){
if(now==null)return optimal;
if(val>now->val)return pred(now->rson,val,now);
else return pred(now->lson,val,optimal);
}
node *succ(node *now,int val,node *optimal){
if(now==null)return optimal;
if(val<now->val)return succ(now->lson,val,now);
else return succ(now->rson,val,optimal);
}
int rk(node *now,int val){
node *p=pred(rt,val,null);
if(p==null)return 1;
splay(p,null);
node *q=succ(rt,val,null);
splay(q,p);
return p->lson->siz+p->cnt+1;
}
node *insert(node *now,int val){
node *p=pred(rt,val,null);
splay(p,null);
node *q=succ(rt,val,null);
splay(q,p);
if(q->lson!=null)q->lson->cnt++,q->lson->siz++;
else {
q->lson=NewNode(val);
q->lson->fa=q;
}
upd(q);
upd(rt);
return q->lson;
}
void remove(node *now,int val){
if(now==null)return ;
node *p=pred(rt,val,null);
splay(p,null);
node *q=succ(rt,val,null);
splay(q,rt);
if(q->lson!=null&&q->lson->val==val){
if(q->lson->cnt>1)q->lson->cnt--,upd(q->lson);
else q->lson=null;
upd(q);
upd(rt);
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
init();
for(int i=1;i<=n;i++){
cin>>opt>>x;
if(opt==1){
node *p=insert(rt,x);
splay(p,null);
}
else if(opt==2){
remove(rt,x);
}
else if(opt==3){
cout<<rk(rt,x)-1<<'\n';
}
else if(opt==4){
node *p=findK(rt,x+1);
cout<<p->val<<'\n';
splay(p,null);
}
else if(opt==5){
node *p=pred(rt,x,null);
if(p->val!=-INF){
cout<<p->val<<'\n';
splay(p,null);
}
}
else{
node *p=succ(rt,x,null);
if(p->val!=INF){
cout<<p->val<<'\n';
splay(p,null);
}
}
}
return 0;
}
文艺平衡树
主要思路:将权值树转化成区间树,通过维护区间的方式完成操作。利用了一个巧妙的思想,将一个区间翻转,相当于将一个区间分成两个区间,然后将内部内容反转,再交换两个区间。
代码贴贴:
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+5,INF=1e8;
int n,opt,x,m;
struct node{
int siz,val,cnt,lf,rt;
node *lson,*rson,*fa;
};
node *rt,nd[N],*null;
bool rev[N];
int gid;
node* NewNode(int val){
nd[++gid].val=val;
nd[gid].siz=1;
nd[gid].cnt=1;
nd[gid].lson=null;
nd[gid].rson=null;
return nd+gid;
}
void upd(node* x){
x->siz=x->cnt;
if(x->lson!=null)x->siz+=x->lson->siz;
if(x->rson!=null)x->siz+=x->rson->siz;
}
void zig(node *x){
node *y=x->fa,*z=y->fa;
y->lson=x->rson;
if(x->rson!=null)x->rson->fa=y;
upd(y);
x->rson=y;
y->fa=x;
upd(x);
x->fa=z;
if(z!=null){
if(z->lson==y)z->lson=x;
else z->rson=x;
}
}
void zag(node* x){
node *y=x->fa,*z=y->fa;
y->rson=x->lson;
if(x->lson!=null)x->lson->fa=y;
upd(y);
x->lson=y;
y->fa=x;
upd(x);
x->fa=z;
if(z!=null){
if(z->lson==y)z->lson=x;
else z->rson=x;
}
}
void splay(node *x,node *T){
while(x->fa!=T){
node *y=x->fa,*z=y->fa;
if(z==T){
if(x==y->lson)zig(x);
else zag(x);
}
else {
bool L=(x==y->lson),R=(y==z->lson);
if(L&&R)zig(y),zig(x);
else if((!L)&&(!R))zag(y),zag(x);
else if(L&&!R)zig(x),zag(x);
else zag(x),zig(x);
}
}
if(x->fa==null)rt=x;
}
void pushdown(node *now){
if(rev[now->val]){
swap(now->lson,now->rson);
rev[now->val]=0;
rev[now->lson->val]^=1;
rev[now->rson->val]^=1;
}
}
node* findK(node* now,int k){
pushdown(now);
int lsize=0;
if(now->lson!=null)lsize=now->lson->siz;
if(k<=lsize)return findK(now->lson,k);
else if(k<=lsize+now->cnt)return now;
else return findK(now->rson,k-lsize-now->cnt);
}
void build(int l,int r,node *f){
if(l>r)return ;
node *mid=NewNode((l+r)>>1);
if((mid->val)<(f->val))f->lson=mid;
else f->rson=mid;
mid->fa=f;
if(l==r)return ;
build(l,mid->val-1,mid);
build(mid->val+1,r,mid);
upd(mid);
}
void init(int l,int r){
int mid=(l+r)>>1;
gid=0;
null=nd+gid;
rt=NewNode(mid);
rt->fa=null;
build(l,mid-1,rt);
build(mid+1,r,rt);
upd(rt);
}
void fz(int l,int r){
node *x=findK(rt,l),*y=findK(rt,r+2);
splay(x,null);splay(y,x);
node *z=y->lson;
rev[z->val]^=1;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
init(1,n+2);
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
fz(l,r);
}
for(int i=2;i<=n+1;i++){
cout<<findK(rt,i)->val -1<<' ';
}
return 0;
}
``

浙公网安备 33010602011771号