各类动态平衡树与可持续化数据结构(坑大,不急)

板子留着,有空补坑
各大常见不常用的平衡树,打的不多,但是至少要了解吧…


##Splay

  • Splay是一种十分高级的平衡树,而与一般的set或者treap不同的是,它是将多次访问的节点尽可能的靠近根的位置,从而减少查询的递归调用次数,而实现这种平衡的方式就是旋转(rotate)了。各个步骤的详解请参看博客 博客详解传送门。每一块都讲的比较的详细,而相应的例题就到各大OJ上去找找吧…
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
struct node{
	int value;
	node *father,*son[2];
	node(int v=0,node *f=NULL){value =v;father=f;son[0]=NULL;son[1]=NULL;}
}*root;
inline bool son(node *f,node *s){return f->son[1]==s;}
inline void rotate(node *t){
	node *f=t->father;
	node *g=f->father;
	bool a=son(f,t),b=!a;
	f->son[a]=t->son[b];
	if(t->son[b]!=NULL) t->son[b]->father=f;
	t->son[b]=f;f->father=t;t->father=g;
	if(g!=NULL) g->son[son(g,f)]=t; else root=t;
}
inline void splay(node *t,node *p){
	while (t->father!=p){
		node *f=t->father;
		node *g=f->father;
		if(g==p) rotate(t);
		else if(son(g,f)^son(f,t)) rotate(t),rotate(t);
				else rotate(f),rotate(t);
	}
}
inline void insert(int val){
	if(root==NULL)root=new node(val,NULL);
	for (node *t=root;t;t=t->son[val>t->value]){
		if(t->value==val){splay(t,NULL);return;}
		if(t->son[val>=t->value]==NULL) t->son[val>=t->value]=new node(val,t);
	}
}
inline void erase(int val){
	node *t=root;
	for (;t;){
		if(t->value==val) break;
		t=t->son[val>t->value];
	}
	if(t!=NULL){
		splay(t,NULL);
		if(t->son[0]==NULL){
			root->son[1];
			if(root!=NULL) root->father=NULL;
		}else{
			node *p=t->son[0];
			while (p->son[1]!=NULL)p=p->son[1];
			splay(p,t);root=p;
			root->father=NULL;
			p->son[1]=t->son[1];
			if(p->son[1]!=NULL) p->son[1]->father=p;
		}
	}
}
int main(){
	
}

##替罪羊树(Scapegoat_Tree)
没基础的小伙伴往这里走:博客传送门代码清晰思路干净,不可多得的一篇好博文。

  • emmm,也是一种十分神奇的平衡树,有三个特点:
  • 1、实现平衡的方式不是旋转,而是直接将不平衡的子树拍散,拉成一条链,从中间二分出一棵新的平衡子树。暴力而优美。
  • 2、判断是否平衡也是利用一个随机数值 $ alpha $ 来实现是否达到平衡,而 a l p h a alpha alpha的选值会对平衡重构的次数与查询次数有所影响。所以比较玄学。
  • 3、判断删除时并不是真正的将他删除,而是定义boollean叫cover来判断是否激活从而进行运算。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define LL long long
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
namespace Scapegoat_Tree{
	#define MAXN (100000 + 10)
	const double alpha =0.75;
	struct Node{
		Node *ch[2];
		int key,size,cover;
		bool exist;
		void PushUp(void){
			size=ch[0]->size+ch[1]->size+(int)exist;
			cover=ch[0]->cover+ch[1]->cover+1;
		}
		bool isBad(void){
			return ((ch[0]->cover>cover*alpha+5)||(ch[1]->cover>cover*alpha+5));
		}
	};
	struct Stree{
		protected:
			Node mem_poor[MAXN];
			Node *tail,*root,*null;
			Node *bc[MAXN];int bc_top;
			Node *NewNode(int key){
				Node *p=bc_top?bc[--bc_top]:tail++;
				p->ch[0]=p->ch[1]=null;
				p->size=p->cover=1;
				p->exist=true;
				p->key=key;
				return p;
			}
			void Travel(Node *p, vector<Node *> &v){
				if(p==null) return;
				Travel(p->ch[0],v);
				if(p->exist) v.push_back(p);
				else bc[bc_top++]=p;
				Travel(p->ch[1],v);
			}
			Node *Divide(vector<Node*>v,int l,int r){
				if(l>=r) return null;
				int mid=(l+r)>>1;
				Node *p=v[mid];
				p->ch[0]=Divide(v,l,mid);
				p->ch[1]=Divide(v,mid+1,r);
				p->PushUp();
				return p;
			}
			void Rebuild(Node * &p){
				static vector <Node *>v;v.clear();
				Travel(p,v);p=Divide(v,0,v.size());
			}
			Node **Insert(Node *&p, int val){
				if(p==null){
					p=NewNode(val);
					return &null;
				}else{
					p->size++;p->cover++;
					Node ** res=Insert(p->ch[val>=p->key],val);
					if(p->isBad()) res=&p;
					return res;
				}
			}
			void Erase(Node *p,int id){
				p->size--;
				int offset=p->ch[0]->size+p->exist;
				if(p->exist&&id==offset){
					p->exist=false;
					return;
				}else{
					if(id<=offset) Erase(p->ch[0],id);
					else Erase(p->ch[1],id-offset);
				}
			}
		public:
			void Init(void){
				tail=mem_poor;
				null=tail++;
				null->ch[0]=null->ch[1]=null;
				null->cover=null->size=null->key=0;
				root=null;bc_top=0;
			}
			Stree(void){Init();}
			
			void Insert(int val){
				Node **p=Insert(root,val);
				if(*p!=null) Rebuild(*p);
			}
			int Rank(int val){
				Node *now=root;
				int ans=1;
				while (now!=null){
					if(now->key>=val) now=now->ch[0];
					else{
						ans+=now->ch[0]->size+now->exist;
						now=now->ch[1];
					}
				}
				return ans;
			}
			int Kth(int k){
				Node *now=root;
				while(now!=null){
					if(now->ch[0]->size+1==k&&now->exist) return now->key;
					else if(now->ch[0]->size>=k) now=now->ch[0];
					else k-=now->ch[0]->size+now->exist,now=now->ch[1];
				}
			}
			void Erase(int k){
				Erase(root,Rank(k));
				if(root->size<alpha*root->cover) Rebuild(root);
			}
			void Erase_Kth(int k){
				Erase(root,k);
				if(root->size<alpha*root->cover) Rebuild(root);
			}
	};
	#undef MAXN
}
int main(){
    
}

##主席树

  • 第一个可持久化数据结构,其实本质是可持久化线段树。学习请参考求区间第k大//主席树
  • 实际上是由多个线段树的叠加组成的,而线段树上所记录的权值是当前子树所包含的离散数据的个数(看上去好像很绕),每插入一个元素就会生成一颗新的线段树,换句话说就是空间换时间吧…
  • 主席树的特点就是可以进行前后状态的加减,从而达到区间的查询,而这里的加减是指对于每一个节点上的权值的相应加减。下面的板子是静态的主席树…不可以修改的…
  • 动态的主席树还需要用到树状数组来进行维护和查询orz…但是我真的不会…
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#define LL long long
using namespace std;
inline int read(){
    int X=0,w=0; char c=0;
    while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return w?-X:X;
}
const int N=200010,LOG=30;
int n,m,q,tot;
int a[N],b[N];
int T[N],sum[N*LOG],L[N*LOG],R[N*LOG];
inline int build(int l,int r){
	int rt=++tot;
	if(l<r){
		int mid=(l+r)>>1;
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	return rt;
}
inline int update(int pre,int l,int r,int x){
	int rt=++tot;
	L[rt]=L[pre],R[rt]=R[pre];sum[rt]=sum[pre]+1;
	if(l<r){
		int mid=(l+r)>>1;
		if(x<=mid) L[rt]=update(L[pre],l,mid,x);
		else R[rt]=update(R[pre],mid+1,r,x);
	}
	return rt;
}
inline int query(int u,int v,int l,int r,int k){
	if(l==r) return l;
	int x=sum[L[v]]-sum[L[u]];
	int mid=(l+r)>>1;
	if(x>=k) return query(L[u],L[v],l,mid,k);
	else return query(R[u],R[v],mid+1,r,k-x);
}
int main(){
    tot=0;
	memset(T,0,sizeof(T));memset(sum,0,sizeof(sum));
	memset(L,0,sizeof(L));memset(R,0,sizeof(R));
	n=read(),q=read();
	for (int i=1;i<=n;i++) a[i]=read(),b[i]=a[i];
	sort(b+1,b+n+1);
	m=unique(b+1,b+n+1)-b-1;
	T[0]=build(1,m);
	for (int i=1;i<=n;i++){
		a[i]=lower_bound(b+1,b+m+1,a[i])-b;
		T[i]=update(T[i-1],1,m,a[i]);
	}
	while (q--){
		int x,y,z;
		x=read(),y=read(),z=read();
		int p=query(T[x-1],T[y],1,m,z);
		printf("%d\n",b[p]);
		
	}
}

##treap(带旋)

  • 下面贴的是洛谷P3369的板子,亲测有效orz
  • 其实treap是最朴实的二叉平衡树了。只有旋转,尽量使树保持平衡,但又没有替罪羊树这么的暴力。~~(当年写的板子代码结构真的丑)~~就当个手打的set抄抄吧_(:з」∠)_
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
const int N=100010;
int n,cnt;
struct node{
    node *L,*R;
    int fix,val,size;
    void init(){
        L=R=NULL;
    }
    inline int lsize(){return L?L->size:0;}
    inline int rsize(){return R?R->size:0;}
}Tree[N],*root;
node *New_node(){
    Tree[cnt].init();
    return Tree+cnt++;
}
void recount(node *&p){
    p->size=p->lsize()+p->rsize()+1;
}
void L_rot(node *&a){
    node *b=a->R;
    a->R=b->L;
    b->L=a;
    a=b;
    recount(a->L);
    recount(a);
}
void R_rot(node *&a){
    node *b=a->L;
    a->L=b->R;
    b->R=a;
    a=b;
    recount(a->R);
    recount(a);
}
void insert(node *&p,int val){
    if(!p){
        p=New_node();
        p->val=val;
        p->size=1;
        p->fix=rand();
    }else if(val<=p->val){
        p->size++;
        insert(p->L,val);
        if(p->L->fix<p->fix){
            R_rot(p);
        }
    }else{
        p->size++;
        insert(p->R,val);
        if(p->R->fix<p->fix){
            L_rot(p);
        }
    }
}
void Delete(node *&p,int val){
    if(val==p->val){
        if(!p->L||!p->R){
            if(p->L) 
                p=p->L;
            else
                p=p->R;
        }else if(p->L->fix<p->R->fix){
            R_rot(p);
            p->size--;
            Delete(p->R,val);
        }else{
            L_rot(p);
            p->size--;
            Delete(p->L,val);
        }
    }else if(val<p->val){
        p->size--;
        Delete(p->L,val);
    }else{
        p->size--;
        Delete(p->R,val);
    }
}
int Rank(node *p,int val){
    if(!p) return 0;
    if(val>p->val){
        return p->lsize()+Rank(p->R,val)+1;
    }else{
        return Rank(p->L,val);
    }
}
int Find(node *p,int rank){
    if(p->lsize()+1==rank) return p->val;
    if(p->lsize()+1>rank) return Find(p->L,rank);
    return Find(p->R,rank-p->lsize()-1);
}
int query_pre(node *p,int val,int now){
    if(!p) return now;
    if(p->val<val) return query_pre(p->R,val,p->val);
    return query_pre(p->L,val,now);
}
int query_after(node *p,int val,int now){
    if(!p) return now;
    if(p->val>val) return query_after(p->L,val,p->val);
    return query_after(p->R,val,now);
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        int t,d;
        scanf("%d%d",&t,&d);
        if(t==1){insert(root,d);continue;}
        if(t==2){Delete(root,d);continue;}
        if(t==3){printf("%d\n",Rank(root,d)+1);continue;}
        if(t==4){printf("%d\n",Find(root,d));continue;}
        if(t==5){printf("%d\n",query_pre(root,d,root->val));continue;}
        if(t==6){printf("%d\n",query_after(root,d,root->val));continue;}
    }
    return 0;
}

###动态树(link-cut-tree)

#include <bits/stdc++.h>
using namespace std;
namespace link_cut_tree{
	#define maxn 100000
	struct node{
		int fa,ch[2];
		bool reverse,is_root;
	}T[maxn];
	struct LCT{
		protected://splay部分 
			int Getson(int x){return x==T[T[x].fa].ch[1];}
			void Push_Reverse(int x){
				if(!x) return;
				swap(T[x].ch[0],T[x].ch[1]);
				T[x].reverse^=true;
			}
			void Push_Down(int x){
				if(T[x].reverse){
					Push_Reverse(T[x].ch[0]);
					Push_Reverse(T[x].ch[1]);
					T[x].reverse=false;
				}
			}
			void Rotate(int x){
				if(T[x].is_root) return;
				int k=Getson(x),f=T[x].fa;
				int g=T[f].fa;
				Push_Down(f);Push_Down(x);
				T[f].ch[k]=T[x].ch[k^1];
				if(T[x].ch[k^1]) T[T[x].ch[k^1]].fa=f;
				T[x].ch[k^1]=f;
				T[f].fa=x;
				T[x].fa=g;
				if(!T[f].is_root) T[g].ch[f==T[g].ch[1]]=x;
				else T[x].is_root=true,T[f].is_root=false;
				//Updata(f);Updata(x);
			}
			void Push(int x){
				if(!T[x].is_root)Push(T[x].fa);
				Push_Down(x);
			}
			void Splay(int x){
				Push(x);
				for (int fa;!T[x].is_root;Rotate(x)){
					fa=T[x].fa;
					if(!T[fa].is_root){
						if (Getson(x)==Getson(fa))Rotate(fa);
						else Rotate(x);
					}
				}
			}
		public:
			void Access(int x){
				int y=0;
				do{
					Splay(x);
					T[T[x].ch[1]].is_root=true;
					T[x].ch[1]=y;
					T[T[x].ch[1]].is_root=false;
					//Updata(x);
					y=x;
					x=T[y].fa;
				}while(x);
			}
			void Mroot(int x){
				Access(x);
				Splay(x);
				Push_Reverse(x);
			}
			void Link(int u,int v){
				Mroot(u);
				T[u].fa=v;
			}
			void cut(int u,int v){
				Mroot(u);
				Access(v);Splay(v);
				Push_Down(v);
				T[u].fa=T[v].ch[0]=0;
			}
	};
	#undef maxn
}
int main(){
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值