平衡树Splay

平衡树

(splay)

又叫伸展树
一种比较玄学的平衡树,实现原理基于更加玄学的(splay)(伸展)函数
理论复杂度为(O(nlogn)),但实现中含较大常数因子
在随机数据下表现最差,众多平衡树中时间效率垫底(跟我一样,菜的离谱
代码实现较为复杂,但其优势也在于它的劣势之处,
正因为有了使常数因子变大的splay函数,也造就了(spaly)强大的区间处理能力,让它在一亿种平衡树中占有一席之地

(Code)

(P3369)
优美的代码……(别人的

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<bitset>
#include<set>
#include<map>
#define LL long long
#define rg register
#define il inline
#define us unsigned
#define eps 1e-6
#define INF 0x3f3f3f3f
#define ls k<<1
#define rs k<<1|1
#define tmid ((tr[k].l+tr[k].r)>>1)
#define nmid ((l+r)>>1)
#define Thispoint tr[k].l==tr[k].r
#define pub push_back
#define lth length
#define pii pair<int,int>
#define mkp make_pair
using namespace std;
inline void Read(int &x){
	int f=1;
	char c=getchar();
	x=0;
	while(c<'0'||c>'9'){
		if(c=='-')f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<3)+(x<<1)+c-'0';
		c=getchar();
	}
	x*=f;
}

template<typename _T>
inline void read(_T &x)
{
	x= 0 ;char s=getchar();int f=1;
	while(s<'0'||'9'<s){f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<1)+(x<<3)+s-'0';s=getchar();}
	x*=f;
 } 
 
const int p=5e5+5;

#define lson(u) t[u].ch[0]
#define rson(u) t[u].ch[1]
#define f(u) t[u].ff
#define v(u) t[u].val
#define c(u) t[u].cnt
#define s(u) t[u].son

int root  = 0,tot = 0;

struct node{
	int ch[2];
	int ff;
	int  val;
	int cnt;
	int son;
	
}t[p]; 

inline void pushup(int u)// 合并函数
{
	s(u)= s(lson(u))+s(rson(u))+c(u);
}

inline void rotate(int x)//旋转函数,zig、zag自动化
{
	int y = f(x);
	int z = f(f(x));// x->fa->fa;
	int k = rson(f(x))==x; // if x is f(u)'s  rson
	t[z].ch[rson(z) == f(x)] = x;f(x) = z;
	t[y].ch[k] = t[x].ch[k^1];f(t[x].ch[k^1]) = y;
	t[x].ch[k^1] = y;f(y) = x; 
	pushup(y);pushup(x);
}

inline void splay(int x,int goal)//高效的伸展操作,对了goal不是你要转到的位置,而是你的目的地的父亲
{
	while(f(x)!=goal)
	{
		int y = f(x);
		int z = f(y);
		if(z!=goal)
		(lson(y) == x)^(lson(z)==y)?rotate(x):rotate(y);
		rotate(x);
	}
	if(goal == 0)
	root = x;
 } 
 
inline void insert(int x)
{
	int u = root,ff = 0;
	while(u&&t[u].val!=x)
	{
		ff = u;
		u = t[u].ch[x > t[u].val];
	}
	if(u) c(u)++;
	else
	{
		u =++tot;
		if(ff) t[ff].ch[x>t[ff].val]  =u;//注意这里的特判是为了插入INF与-INF,防止0节点有儿子,否则可以预料会WA的莫名其妙
		lson(tot) =rson(tot) = 0;
		f(tot) = ff;v(tot)= x;
		c(tot)=1;s(tot)=1;
	}
	splay(u,0);
}

void Find(int x)
{
	int u = root;
	if(!u) return ;
	while(t[u].ch[x > v(u)]&&(x!=v(u)))  u = t[u].ch[x>v(u)];
	splay(u,0);
}

int prenxt(int x,int f)
{
	Find(x);
	int u =root;
	if((v(u)>x&&f)||((v(u)<x)&&!f)){return u;}
	u = t[u].ch[f];
	while(t[u].ch[f^1]) u= t[u].ch[f^1];
	return u;
}

void Delete(int x)
{
	int last = prenxt(x,0);
	int nxt = prenxt(x,1);
	splay(last,0);
	splay(nxt,last);
	int del = lson(nxt);
	if(c(del) > 1)
	{
		c(del)--;
		splay(del,0);
	}
	else lson(nxt) = 0;
}

int k_th(int x)
{
	int u =root;
	if(s(u)<x) return 0;
	while(23333)
	{
		int y = lson(u);
		if(x>c(u)+s(y))
		{
			x-=c(u)+s(y);
			u = rson(u);
		}
		else
			if(s(y)>=x) u = y;
			else return v(u);
	}
}

signed main()
{
	int n;
	
	insert(-INF);
	insert(INF);
	read(n);
	
	for(int i=1;i<=n;i++)
	{
		int opt;
		read(opt);
		int x;
		read(x);
		if(opt == 1) {insert(x);}
		if(opt == 2){Delete(x);}
		if(opt==3){	Find(x);printf("%d
",s(lson(root)));}
		if(opt == 4){printf("%d
",k_th(x+1));}
		if(opt == 5){printf("%d
",v(prenxt(x,0)));}
		if(opt == 6){printf("%d
",v(prenxt(x,1)));}
		
	}
}

丑陋的指针版代码(不过这个好像比上面的快(48ms)

#include<iostream>
#include<cstdio>
#include<cstring>

using namespace std;

#define int long long
#define INF 1<<30

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}

struct node{
	int key;
	int sum;
	int cnt;
	node *ls,*rs,*fa;	
}; 

node mem[233333],*pool = mem,*aux,*rot;
node *New(){return pool++;}

void hb(node *a)
{
	a->sum = a->cnt;
	if(a->ls)
	a->sum += a->ls->sum ;
	if(a->rs) 
	a->sum+= a->rs->sum;
}

void zig(node *x)
{
	node *y = x->fa;x->fa = y->fa;
	y->ls = x->rs;
	if(y->ls) y->ls->fa = y;
	y->fa = x;x->rs = y;
	hb(y);hb(x);
	if(x->fa!=NULL)
	{
		if(x->fa->ls == y)
			x->fa->ls=x;
		else x->fa->rs = x;
	}
}

void zag(node *x)
{
	node *y=x->fa;x->fa=y->fa;
	y->rs = x->ls;
	
	if(y->rs) y->rs->fa = y;
	
	y->fa = x;x->ls = y;
	hb(y);hb(x);
	if(x->fa!=NULL)
	{
		if(x->fa->ls == y)
			x->fa->ls=x;
		else x->fa->rs = x;
	}
}
inline void splay(node *x,node *S)
{
	if(x == S) return ;
	node *op =S->fa;
	while(23333)
	{
		node *y = x->fa;
		node *z = x->fa->fa;
		if(y == S)
		{
			if(y->ls == x) zig(x);
			else zag(x);			
			break;
		}

		if(y->ls == x)
		{
			if(z->ls == y)
			{
				zig(y);
				zig(x);
			}
			else
			{
				zig(x);
				zag(x);
			}
		}
		else
		{
			if(z->ls == y)
			{		
				zag(x);
				zig(x);
			}
			else
			{
				zag(y);
				zag(x);
			}
		}
		if(z == S) 
		{
			break;
		}
	}
}

node *make(int val)
{
	node *s= New();
	s->key = val;
	s->sum = 1;
	s->cnt =1;
	s->rs = s->ls=NULL;
}
inline void dh(node *z)
{
	int ui ;
	if(z->key == 451009)
	{
	cout<<(z->key)<<" ";
	cout<<(&z);		
	}

}
int pity;
inline node *insert(int x,node *op)
{
	
	node *splay;
	if(op->key==x) 
	{
		op->cnt++;
		hb(op);
		return op;
	}
	if(op->key<x){
		if(!op->rs){op->rs=make(x);op->rs->fa = op;splay = op->rs;}
		else {splay = insert(x,op->rs);}
	}
	if(op->key>x){
		if(!op->ls){op->ls=make(x);op->ls->fa = op;splay = op->ls;}
		else {splay = insert(x,op->ls);}		
	}
	hb(op);
	return splay;
}

inline node *Find(int x,node *op)
{
	if(op->key!=x)
	{
		if(op->key < x) return Find(x,op->rs);
		if(op->key > x) return Find(x,op->ls);
	}
	else return op;
}

inline int qRank(int x)
{
	node *op = Find(x,rot);
	splay(op,rot);
	rot = op;
	if(op->ls) return op->ls->sum+1;
	else return 1; 
}

inline int rankq(int x,node *sp)
{
	int ans=0;
	if(sp->ls) ans = sp->ls->sum;
	else ans = 0;
	
	if(ans+sp->cnt < x){return rankq(x- ans - sp->cnt,sp->rs);}
	else
	{
		if(x<=ans) return rankq(x,sp->ls);
		else return sp->key;
	}
}

inline node *mAx(node *s){return (s->rs)? mAx(s->rs) : s;}
inline node *mIn(node *s){return (s->ls)? mIn(s->ls) : s;}

inline void Delete(int x)
{
	node *op = Find(x,rot);
	splay(op,rot);
	rot = op;
	if(rot->cnt > 1) {rot->cnt--;hb(rot);return;}
	if(!rot->ls){rot = rot->rs;rot->fa = 0;return;}
	node *s = mAx(rot->ls);
	splay(s,rot->ls);
	s->rs = rot->rs;
	if(s->rs)s->rs->fa = s;
	s->fa=0;
	hb(s);
	rot = s;
}

inline int pre(int x)
{
	node op = *rot;
	int ans = -INF;
	while(23333)
	{
		if(op.key < x) 
		{
			ans = max(op.key , ans);
			if(op.rs!=NULL) op = *op.rs;
			else break;
		}
		else 
		{
			if(op.ls!=NULL)op = *op.ls;
			else break;
		}
	}
	return ans;	
}

inline int nxt(int x)
{
	node op = *rot;
	int ans = INF;
	while(2333)
	{
		if(op.key > x) 
		{
			ans = min(op.key , ans);
			if(op.ls!=NULL) op = *op.ls;
			else break;
		}
		else 
		{
			if(op.rs!=NULL)op = *op.rs;
			else break;
		}
	}
	return ans;	
}

signed main()
{
	rot = make(INF);
	rot->sum = rot->cnt = 0;
	insert(-INF,rot);
	rot->ls->sum = rot->ls->cnt = 0;
	hb(rot);
	int n;
	
	read(n);
	
	for(int i=1,op,val;i<=n;i++)
	{
		pity = i;
		read(op);
		read(val);
		if(op == 1) 
		{
			aux = insert(val,rot);
			splay(aux,rot);
			rot = aux;	
		}
		if(op == 2)	Delete(val);
		if(op == 3) printf("%lld
",qRank(val));
		if(op == 4) printf("%lld
",rankq(val,rot));//cout<<(qrank(val))<<'
';
		if(op == 5) printf("%lld
",pre(val));
		if(op == 6) printf("%lld
",nxt(val));
		
		
	}	

文艺平衡树

要求实现区间翻转操作,
这个题我一开始一直都想不明白怎么用splay维护
直到看了这篇博客
突然意识到自己思维僵化严重……
根本想不到跳出值域平衡树的圈子

总之就是在(splay)的时候不再在意每个点的权值,这样才能从一棵至于平衡树进化为区间树

(detail)

维护序列前插入哨兵节点(1)(n+2)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstring>

using namespace std;

#define int long long
#define INF 1<<30

template<typename _T>
inline void read(_T &x)
{
	x=0;char s=getchar();int f=1;
	while(s<'0'||s>'9') {f=1;if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	x*=f;
}
int n,m;

#define lson(x) t[x].ch[0]
#define rson(x) t[x].ch[1]
#define s(x) t[x].size
#define vl(x) t[x].v
#define f(x) t[x].ff
#define tag(x) t[x].mark

const int p=1e5+5;

struct Node{
	int ch[2];
	int ff,v;
	int size;
	int mark;
	
	void init(int x,int fa)
	{
		ch[0] = ch[1] = 0;
		size = 1;v = x;ff =fa;
	}
}t[p];

int root,M,tot;

inline void pushup(int x)
{
	s(x) = s(lson(x))+s(rson(x))+1;	
}

inline void pushdown(int x)
{
	if(t[x].mark)
	{
		tag(lson(x))^=1;
		tag(rson(x))^=1;
		tag(x) = 0;
		swap(lson(x),rson(x));
	}
}

inline void rotate(int x)
{
	int y = f(x);
	int z = f(y);
	int k = rson(y)==x;
	t[z].ch[rson(z)==y] = x;f(x) = z;
	t[y].ch[k] = t[x].ch[k^1];f(t[x].ch[k^1]) = y;
	t[x].ch[k^1] = y;f(y) = x;
	pushup(y);
	pushup(x);
}

inline void splay(int x,int goal)
{
	while(f(x)!=goal)
	{
		int z = f(f(x));
		int y = f(x);
		if(z!=goal) (rson(z)==y)^(rson(y) == x)?rotate(x):rotate(y);
		rotate(x);
	}
	if(goal == 0) root = x;
}

inline void insert(int x)
{
	int u = root;
	int fa = 0;
	while(u){fa = u;u = t[u].ch[vl(u) < x];}
	u = ++tot;
	if(fa) t[fa].ch[vl(fa) < x] = u;
	t[u].init(x,fa);
	splay(u,0);
}

inline int k_th(int x)
{
	int u =root;
	while(2333)
	{
		pushdown(u);
		int y = lson(u);
		pushup(u);
		if(x <= s(y)) {u = lson(u);continue;}
		if(s(y)+1 == x) return u;
		else  x-=s(y)+1,u = rson(u);
		
	}
}

void print(int u)
{
	pushdown(u);
	if(lson(u)) print(lson(u));
	if(vl(u)>1 && vl(u)<n+2) printf("%d ",vl(u)-1);
	if(rson(u)) print(rson(u));
 } 

inline void solve(int l,int r)
{
	l =k_th(l);
	r = k_th(r+2);
	splay(l,0);
	splay(r,l);
	t[lson(rson(root))].mark^=1;
 } 

signed  main()
{
	int m;
	read(n);read(m);
	for(int i=1;i<=n+2;i++) insert(i);
	for(int i=1,a,b;i<=m;i++)
	{
		read(a);
		read(b);
		solve(a,b);
	}
	print(root);	
	printf("
");	
	

 }

(End) and 后记

(WC)我听了个寂寞,……
真就划呗……

原文地址:https://www.cnblogs.com/-Iris-/p/14359248.html