【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树
平衡树的几种姿势: AVL Red&Black_Tree 码量爆炸,不常用;SBT 出于各种原因,不常用。
常用:
Treap 旋转 基于旋转操作和随机数堆 但不支持区间操作。
非旋转 基于随机数堆和拆分合并操作 常数较大
时间复杂度:很难被卡,均摊O(logN)
#include<cstdio> #include<iostream> #include<cstdlib> #define MAXN 100005 using namespace std; int ch[MAXN][2],key[MAXN],r[MAXN],size[MAXN],cnt[MAXN],root,sz,n,m; inline void update(int x) { size[x]=size[ch[x][1]]+size[ch[x][0]]+cnt[x]; } inline void rotate(int &x) { int wh=r[ch[x][1]]>r[ch[x][0]],son=ch[x][wh]; ch[x][wh]=ch[son][wh^1]; ch[son][wh^1]=x; update(x); update(son); x=son; } void insert(int &now,int x) { if(!now) { now=++sz; cnt[sz]=size[sz]=1; key[sz]=x; r[sz]=(rand()%100+rand()); return; } if(key[now]==x) { cnt[now]++; size[now]++; return; } insert(ch[now][key[now]<x],x); if(r[now]<r[ch[now][key[now]<x]]) rotate(now); else update(now); } void del(int &now,int x) { if(key[now]==x) { if(cnt[now]>1) { cnt[now]--; size[now]--; return; } if(ch[now][1]*ch[now][0]==0) { now=ch[now][1]+ch[now][0]; return; } rotate(now); del(ch[now][key[ch[now][1]]==x],x); update(now); return; } del(ch[now][key[now]<x],x); update(now); } inline int kth(int x) { int now=root; while(1) { if(size[ch[now][0]]>=x) { now=ch[now][0]; continue; } int lon=cnt[now]+size[ch[now][0]]; if(lon>=x) return key[now]; x-=lon; now=ch[now][1]; } } inline int rank(int x) { int now=root,ans=0; while(1) { if(key[now]==x) return ans+1+size[ch[now][0]]; if(x<key[now]) { now=ch[now][0]; continue; } ans+=cnt[now]+size[ch[now][0]]; now=ch[now][1]; } } inline int pre(int x) { int now=root,ans=-0x7fffffff; while(now) if(key[now]<x) { if(key[now]>ans) ans=key[now]; now=ch[now][1]; } else now=ch[now][0]; return ans; } inline int next(int x) { int now=root,ans=0x7fffffff; while(now) if(key[now]>x) { if(key[now]<ans) ans=key[now]; now=ch[now][0]; } else now=ch[now][1]; return ans; } int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); scanf("%d",&n); while(n--) { int x,y; scanf("%d%d",&y,&x); switch(y) { case 1:insert(root,x);break; case 2:del(root,x);break; case 3:printf("%d\n",rank(x));break; case 4:printf("%d\n",kth(x));break; case 5:printf("%d\n",pre(x));break; case 6:printf("%d\n",next(x));break; } } return 0; }
#include<cstdio> #include<cstring> #include<iostream> #include<ctime> #include<cstdlib> #define MAXN 100010 using namespace std; inline int read() { int sum=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(); } return sum*f; } struct Treap { struct Node { Node *ch[2]; int key,v,size; void pushup() { size=ch[1]->size+ch[0]->size+1; } }null[MAXN],*root,*stack[MAXN]; int top; void Init() { top=0; root=null; null->ch[1]=null->ch[0]=null; for(int i=1;i<MAXN;i++)stack[++top]=null+i; } Node *New(int key) { Node *p=stack[top--]; p->ch[1]=p->ch[0]=null; p->size=1; p->key=key; p->v=rand(); return p; } Node *Merge(Node *a,Node *b) { if(a==null)return b; if(b==null)return a; if(a->v<b->v) { a->ch[1]=Merge(a->ch[1],b); a->pushup(); return a; } else { b->ch[0]=Merge(a,b->ch[0]); b->pushup(); return b; } } pair<Node*,Node*> split(Node *x,int k) { if(x==null)return make_pair(null,null); if(x->ch[0]->size>=k) { pair<Node*,Node*> y=split(x->ch[0],k); x->ch[0]=y.second; x->pushup(); y.second=x; return y; } else { pair<Node*,Node*> y=split(x->ch[1],k-x->ch[0]->size-1); x->ch[1]=y.first; x->pushup(); y.first=x; return y; } } int getrank(Node *p,int key) { if(p==null)return 0; return p->key>=key?getrank(p->ch[0],key):getrank(p->ch[1],key)+p->ch[0]->size+1; } int getkth(int k) { Node *now=root; while(1) if(now->ch[0]->size>=k) now=now->ch[0]; else if(now->ch[0]->size+1==k) return now->key; else k-=now->ch[0]->size+1,now=now->ch[1]; } void insert(int key) { int k=getrank(root,key); pair<Node*,Node*> x=split(root,k); Node *p=New(key); root=Merge(Merge(x.first,p),x.second); } void del(int key) { int k=getrank(root,key); pair<Node*,Node*> x=split(root,k); pair<Node*,Node*> y=split(x.second,1); stack[++top]=y.first; root=Merge(x.first,y.second); } int prefix(int key) { return getkth(getrank(root,key)); } int suffix(int key) { return getkth(getrank(root,key+1)+1); } }YY; int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); YY.Init(); int T=read(); while(T--) { int opt=read(); int x=read(); switch(opt) { case 1:YY.insert(x); break; case 2:YY.del(x); break; case 3:printf("%d\n",YY.getrank(YY.root,x)+1); break; case 4:printf("%d\n",YY.getkth(x)); break; case 5:printf("%d\n",YY.prefix(x)); break; case 6:printf("%d\n",YY.suffix(x)); break; } } return 0; }
PS:非旋转可以实现平衡树的可持久化,还能来套一些东西
Spaly 完全基于旋转 各种操作
时间复杂度:引用:“ 称单旋无神犇,双旋O(logN),这句话我也没有考证,个人表示不想做什么太多的探究”。毕竟Splay的复杂度本来就挺玄学的了,而且专门卡单旋Splay的题也没怎么听说过。
#include<cstdio> #define MAXN 200005 using namespace std; int key[MAXN],ch[MAXN][2],cnt[MAXN],size[MAXN],f[MAXN],n,sz,root; inline void clear(int x) { key[x]=ch[x][0]=ch[x][1]=cnt[x]=size[x]=f[x]=0; } inline int get(int x) { return ch[f[x]][1]==x; } inline void update(int x) { size[x]=cnt[x]+size[ch[x][1]]+size[ch[x][0]]; } inline void rotate(int x) { int fa=f[x],pa=f[fa],what=get(x); if(pa){ch[pa][fa==ch[pa][1]]=x;} ch[fa][what]=ch[x][what^1]; f[ch[fa][what]]=fa; ch[x][what^1]=fa; f[fa]=x; f[x]=pa; update(fa); update(x); } inline void splay(int x) { for(int fa;(fa=f[x]);rotate(x)) if(f[fa]) rotate((get(x)==get(fa)?fa:x)); root=x; } inline void insert(int x) { if(!root) { sz++; ch[sz][1]=ch[sz][0]=f[sz]=0; key[sz]=x; cnt[sz]=size[sz]=1; root=sz; return; } int now=root,fa=0; while(1) { if(key[now]==x) { cnt[now]++; splay(now); return; } fa=now; now=ch[now][key[now]<x]; if(!now) { sz++; f[sz]=fa; ch[sz][1]=ch[sz][0]=0; key[sz]=x; cnt[sz]=1; ch[fa][key[fa]<x]=sz; splay(sz); return; } } } inline void find(int x) { int now=root; while(1) { if(x<key[now]) { now=ch[now][0]; continue; } if(key[now]==x) { splay(now); return; } now=ch[now][1]; } } inline int rank(int x) { find(x); return size[ch[root][0]]+1; } inline int findx(int x) { int now=root; while(1) { if(size[ch[now][0]]>=x) { now=ch[now][0]; continue; } int lon=size[ch[now][0]]+cnt[now]; if(x<=lon) return key[now]; x-=lon; now=ch[now][1]; } } inline void del(int x) { find(x); if(cnt[root]>1){cnt[root]--;return;} if(size[root]==1){clear(root);root=0;return;} if (!ch[root][0]){ int oldroot=root;root=ch[root][1];f[root]=0;clear(oldroot);return; } else if (!ch[root][1]){ int oldroot=root;root=ch[root][0];f[root]=0;clear(oldroot);return; } int now=ch[root][0],old=root; while(ch[now][1])now=ch[now][1]; f[ch[old][1]]=now; ch[now][1]=ch[old][1]; f[ch[old][0]]=0; clear(old); splay(now); return; } inline int pre(int x) { insert(x); int now=ch[root][0]; while(ch[now][1])now=ch[now][1]; del(x); return key[now]; } inline int next(int x) { insert(x); int now=ch[root][1]; while(ch[now][0])now=ch[now][0]; del(x); return key[now]; } int main() { scanf("%d",&n); while(n--) { int x,y; scanf("%d%d",&y,&x); switch(y) { case 1:insert(x);break; case 2:del(x);break; case 3:printf("%d\n",rank(x));break; case 4:printf("%d\n",findx(x));break; case 5:printf("%d\n",pre(x));break; case 6:printf("%d\n",next(x));break; } } return 0; }
ScapeGoat_Tree 基于a权值平衡树和压扁重构 无旋转 但不支持区间操作;运用a权值平衡树一定是a高度平衡树来维持log的效率;主要操作就是拍扁重建,但是为了解决删除时的繁冗讨论所以维持一个带有已删除点的残树,维持这棵树的高度,同时在维护时维护这棵树的残点不超过一定比例;如果不用cnt那么删除时一定要用找排名的方式,这样的话会防止原来的一子链重建后成为人字链
时间复杂度:最坏会被卡到O(n2)(实际上∑(㏒₂n/i)*(㏒₂i)*i)(只是重建)但是那是把区间从大到小输入(或相反),实际上这是不加常数的结果(此处常数指替罪羊判断不平衡时除了alpha以外的那个常数),一般会是均摊logn(CTR会为了性命而不敢去卡)
#include<cstdio> #include<iostream> using namespace std; const int MAXN=200010; const double a=0.75; struct node { node *ch[2]; int key,size,cover,ex; inline void update() { size=ch[0]->size+ch[1]->size+ex; cover=ch[0]->cover+ch[1]->cover+1; } inline bool bad() { return ch[0]->cover>=cover*a+5||ch[1]->cover>=cover*a+5; } }Mem[MAXN],*null,*root,*stack[MAXN],*lst[MAXN]; int len,top; inline void Init() { root=null=Mem; null->size=null->cover=null->ex=0; null->ch[0]=null->ch[1]=Mem; for(int i=1;i<MAXN;i++)stack[++top]=Mem+i; } inline node *New(int key) { node *t=stack[top--]; t->ch[0]=t->ch[1]=null; t->size=t->cover=t->ex=1; t->key=key; return t; } inline void travel(node *p) { if(p==null)return; travel(p->ch[0]); if(p->ex)lst[++len]=p; else stack[++top]=p; travel(p->ch[1]); } inline node *divide(int l,int r) { if(l>r)return null; int mid=(l+r)>>1; lst[mid]->ch[0]=divide(l,mid-1); lst[mid]->ch[1]=divide(mid+1,r); lst[mid]->update(); return lst[mid]; } inline void rebuild(node *&p) { len=0; travel(p); p=divide(1,len); } inline node **insert(node *&p,int key) { if(p==null) { p=New(key); return &null; } p->size++; p->cover++; node **ret=insert(p->ch[p->key<=key],key); if(p->bad())ret=&p; return ret; } inline void erace(node *p,int k) { //cout<<p->ch[0]->size<<endl; p->size--; if(p->ex&&k==p->ch[0]->size+1) { p->ex=0; return; } if(k<=p->ch[0]->size)erace(p->ch[0],k); else erace(p->ch[1],k-p->ch[0]->size-p->ex); } inline int Kth(int k) { node *p=root; while(p!=null) { if(p->ex&&k==p->ch[0]->size+1)return p->key; else if(p->ch[0]->size>=k)p=p->ch[0]; else k-=p->ch[0]->size+p->ex,p=p->ch[1]; } } inline int Rank(int x) { node *p=root; int ret=1; while(p!=null) if(p->key>=x) p=p->ch[0]; else ret+=p->ch[0]->size+p->ex,p=p->ch[1]; return ret; } inline void Insert(int x) { node **p=insert(root,x); if(*p!=null)rebuild(*p); } inline void Erace_kth(int k) { erace(root,k); if(root->size<root->cover*a)rebuild(root); } inline void Erace(int x) { Erace_kth(Rank(x)); } int main() { freopen("phs.in","r",stdin); freopen("phs.out","w",stdout); Init(); int Q,opt,x; scanf("%d",&Q); while(Q--) { scanf("%d%d",&opt,&x); switch(opt) { case 1:Insert(x);break; case 2:Erace(x);break; case 3:printf("%d\n",Rank(x));break; case 4:printf("%d\n",Kth(x));break; case 5:printf("%d\n",Kth(Rank(x)-1));break; case 6:printf("%d\n",Kth(Rank(x+1)));break; } } }
打了四种平衡树,发现01Trie最快还™短..........
#include <cstdio> using namespace std; const int A=24,fix=10000000; inline void read (int &now){ register char word=getchar();bool temp=false; for(now=0;word<'0'||word>'9';word=getchar())if(word=='-')temp=true; for(;word>='0'&&word<='9';now=(now<<1)+(now<<3)+word-'0',word=getchar()); if(temp)now=-now; } struct Trie{ Trie *ch[2];int size; void* operator new(size_t); }*root,*null,*C,*mempool; void* Trie :: operator new(size_t){ if(C==mempool)C=new Trie[(1<<21)+10],mempool=C+(1<<21)+10; return C++; } inline Trie *New(){ register Trie *p=new Trie; p->ch[0]=p->ch[1]=null,p->size=0; return p; } int n; inline void Insert(int x,int size){ register Trie *p=root;x+=fix; for(int i=A;i>=0;i--){ if(p->ch[(x>>i)&1]==null)p->ch[(x>>i)&1]=New(); p=p->ch[(x>>i)&1];p->size+=size; } } inline int get_Rank(int x){ register int ret=0;x+=fix;register Trie *p=root; for(register int i=A;i>=0&&p!=null;i--) if(x&(1<<i))ret+=p->ch[0]->size,p=p->ch[1]; else p=p->ch[0]; return ret; } inline int get_Kth(int k){ register Trie *p=root;register int ret=0; for(register int i=A;i>=0;i--) if(p->ch[0]->size>=k)p=p->ch[0]; else ret|=(1<<i),k-=p->ch[0]->size,p=p->ch[1]; return ret-fix; } int main(){ freopen("phs.in","r",stdin);freopen("phs.out","w",stdout); null=new Trie,null->ch[0]=null->ch[1]=null,null->size=0,root=new Trie,root->ch[0]=root->ch[1]=null,root->size=0; read(n);register int opt,x; while(n--){ read(opt),read(x); switch(opt){ case 1:Insert(x,1);break; case 2:Insert(x,-1);break; case 3:printf("%d\n",get_Rank(x)+1);break; case 4:printf("%d\n",get_Kth(x));break; case 5:printf("%d\n",get_Kth(get_Rank(x)));break; case 6:printf("%d\n",get_Kth(get_Rank(x+1)+1));break; } } }