板子留着,有空补坑
各大常见不常用的平衡树,打的不多,但是至少要了解吧…
##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(){
}