(笔记)Splay

二叉查找树定义

  1. 左子树所有节点权值小于根节点权值

  2. 右子树所有节点权值大于根节点权值

  3. 以左右子节点为根节点时子树同样满足上述条件

Splay(又称伸展树)

(二叉查找树)

伸展操作 \(Splay(x)\)

把一个节点拎到它所在 Splay 的根的操作。

  1. Zig(右旋)操作

令x为y的左儿子,则使y的左儿子指向x的右儿子,x的右儿子指向y。

  1. Zag(左旋)操作

令x为y的右儿子,则使y的右儿子指向x的左儿子,x的左儿子指向y。

上述操作互逆

根据实际情况所需进行zig、zag、zig-zig、zag-zag、zig-zag操作,组合即得到 Splay(x) 函数。

排名、查找前驱后继等函数

合理利用splay函数与二叉搜索树性质即可。

一些模板

普通平衡树

模板:P3369【模板】普通平衡树

你会发现其实这个东西很好写,只要利用一些小技巧就可以不拆开 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;
}

文艺平衡树

模板:P3391 【模板】文艺平衡树

主要思路:将权值树转化成区间树,通过维护区间的方式完成操作。利用了一个巧妙的思想,将一个区间翻转,相当于将一个区间分成两个区间,然后将内部内容反转,再交换两个区间。

代码贴贴:

#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;
}
``
posted @ 2025-04-24 14:52  TBSF_0207  阅读(24)  评论(0)    收藏  举报