!前置技能&概念!ios
二叉搜索树git
一棵二叉树,对于任意子树,知足左子树中的任意节点对应元素小于根的对应元素,右子树中的任意节点对应元素大于根对应元素。换言之,就是知足中序遍历为依次访问节点对应元素为升序的二叉树。
ui
平衡树spa
一棵二叉搜索树,为了防止插入、查询等在朴素二叉搜索树中复杂度为$O(Dep)$的操做在极端数据下会$TLE$,而在操做中不断经过旋转等操做使得树的形态更加平衡,并知足中序遍历不变。code
$Treap$blog
一棵基于给每一个点随机分配权值并维护堆结构以保持树结构较为平均的平衡树递归
无旋$Treap$get
不使用旋转而不断分裂与合并来代替其余操做的$Treap$string
合并it
两棵非旋$Treap\space A\space B$能合并,当且仅当$A$中的全部元素均小于$B$中的全部元素。
合并时,先保证堆的结构,即根为$A,B$两根中随机分配的值较小(本文以小根堆为例)
若将元素小(不妨设为$A$)的根$Root_A$做为根,考虑到$A$中元素小于$B$,则将$Root_A$的右儿子所在子树与$B$合并,并做为$A$的新右儿子。
反之,若将元素大(不妨设为$B$)的根$Root_B$做为根,考虑到$B$中元素大$A$,则将$Root_B$的左儿子所在子树与$A$合并,并做为$B$的新左儿子。
而后递归处理便可。不难发现,每一次合并都同时保证了堆和平衡树的结构。
#define ls c[x][0] #define rs c[x][1] #define lt c[y][0] #define rt c[y][1] int merge(int x,int y){ if(!x||!y) return x|y; if(K[x]>K[y]){lt=merge(x,lt),pushup(y);return y;} rs=merge(rs,y),pushup(x); return x; //K值为每一个点被随机分配的值 //注意:在无旋Treap中是不须要记录每一个点的父节点的 }
分裂
将以$x$为根的$Treap$分裂为两个$Treap\space A\space B$,其中任意$A$中元素都小于全部$B$中的元素。
每次操做获得两个$Treap$必定须要返回两个数,我习惯用结构体...
说正经的,若咱们要将以$x$为根的$Treap$前$K$个分为一组,后$N-K$个分为一组,先判断若左子树的$Size$已经不小于$K$了,那么前$K$个必定所有在左子树中产生,咱们递归将左子树分为前$K$个为一组,剩下的为一组,这样$x$即$x$的右子树必定属于那$N-K$个,咱们只须要把左子树中剩下的那部分做为$x$的左子树便可。若左子树的$Size$小于$K$,那么至少左子树和$x$必定属于前$K$个,设左子树的$Size=SizeL$,那么只须要递归将$x$的右子树划分为前$K-SizeL-1$个分为一组,并把这些连成$x$的新右子树便可。当$x$为空时,两个根都是空,返回便可。
#define ls c[x][0] #define rs c[x][1] struct Droot{int A,B;}; Droot split(int x,int rk){ Droot tmp; if(!x){tmp.A=tmp.B=0;return tmp;} if(sz[ls]>=rk) tmp=split(ls,rk),ls=tmp.B,pushup(x),tmp.B=x; else tmp=split(rs,rk-sz[ls]-1),rs=tmp.A,pushup(x),tmp.A=x; return tmp; }
无旋$Treap$的基本操做大体就是这两个,其余平衡树的常规操做均可以创建在它的基础上进行。
插入&删除
若要插入,在$pos$的位置先后分裂成两棵,再创造一个要插入的新的节点,最后依次合并便可。
若要删除,在$pos-1$的位置先后分裂成两棵,再在后一棵分裂出前一个,把这个点忽略,把剩下两棵$Treap$合并便可。
单点区间$\rightarrow$查询修改
把与当前要查询的部分无关的前缀和后缀都分裂出去,而后就能够进行操做和询问,最后再合并回去
BZOJ1014 火星人
用平衡树动态维护哈希值
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define M 400020 #define bas 29 #define ls c[x][0] #define rs c[x][1] #define lt c[y][0] #define rt c[y][1] using namespace std; int read(){ int nm=0,fh=1; char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } void write(int x){if(x>9) write(x/10);putchar(x%10+'0');} char ch[M],S[20]; int sed=67109281,n,m,c[M][2],K[M],sz[M],u,v; int tot,H[M],P[M],Root,p[M],CT; int RAND(){return sed=(LL)sed*17%998244353;} void pushup(int x){sz[x]=sz[ls]+sz[rs]+1,H[x]=H[ls]*P[sz[rs]+1]+p[x]*P[sz[rs]]+H[rs];} struct Droot{int A,B;}; int nw(int x){++tot,K[tot]=RAND(),p[tot]=x,pushup(tot);return tot;} int merge(int x,int y){ if(!x||!y) return x|y; if(K[x]>K[y]){lt=merge(x,lt),pushup(y);return y;} rs=merge(rs,y),pushup(x); return x; } Droot split(int x,int rk){ Droot tmp; if(!x){tmp.A=tmp.B=0;return tmp;} if(sz[ls]>=rk) tmp=split(ls,rk),ls=tmp.B,pushup(x),tmp.B=x; else tmp=split(rs,rk-sz[ls]-1),rs=tmp.A,pushup(x),tmp.A=x; return tmp; } void ins(int x,int pos){ Droot tmp=split(Root,pos); x=nw(x); Root=merge(tmp.A,x),Root=merge(Root,tmp.B); } void change(int x,int pos){ Droot tmp=split(Root,pos-1),ot; ot=split(tmp.B,1); p[ot.A]=x,pushup(ot.A),Root=merge(tmp.A,ot.A),Root=merge(Root,ot.B); } int getnum(int l,int r){ Droot tmp=split(Root,r); Droot ot=split(tmp.A,l-1); int fin=H[ot.B]; Root=merge(ot.B,tmp.B),Root=merge(ot.A,Root); return fin; } int getans(int t1,int t2){ if(t1==t2) return tot-t1+1; int l=0,r=tot-max(t1,t2),md,fin=0,ans1,ans2; while(l<=r){ md=((l+r)>>1),ans1=getnum(t1,t1+md),ans2=getnum(t2,t2+md); if(ans1!=ans2) r=md-1; else fin=l=md+1; } return fin; } int build(int l,int r){ int x=((l+r)>>1),now=++tot; CT+=10,K[now]=CT,p[now]=ch[x]-'a'; if(l<x) c[now][0]=build(l,x-1); if(x<r) c[now][1]=build(x+1,r); pushup(now); return now; } int main(){ scanf("%s",ch+1),n=strlen(ch+1),P[0]=1; for(int i=1;i<=n;i++) P[i]=P[i-1]*bas; Root=build(1,n); for(int T=read();T;T--){ scanf("%s",S); if(S[0]=='Q') u=read(),v=read(),m=getans(u,v),printf("%d\n",m); else if(S[0]=='I') u=read(),scanf("%s",S),ins(S[0]-'a',u); else u=read(),scanf("%s",S),change(S[0]-'a',u); } return 0; }
无旋$Treap$还有一个很是强大的功能——可持久化
不难发现,没有旋转,每次修改仅是左右儿子中的一个,那么就尝试进行可持久化。
每次修改不选择直接连上新的左右儿子,而是复制新的节点做为左右儿子。
放上支持访问历史版本的文艺平衡树。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define LL long long #define mid ((l+r)>>1) #define ls c[x][0] #define rs c[x][1] #define M 50020 using namespace std; int read(){ int nm=0,fh=1;char cw=getchar(); for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0'); return nm*fh; } int n,m,c[M*300][2],sz[M*300],rev[M*300],rt[M*300],ti,T,tpe,at; int sum[M*300],nd[M*300],p[M*300],cnt,sed=21854203,u,v,t[M]; struct Droot{int A,B;}; int rd(){return sed=abs(sed*547-93723893);} void pushup(int x){sz[x]=sz[ls]+sz[rs]+1,sum[x]=sum[ls]+sum[rs]+p[x];} int creat(int num){cnt++,sum[cnt]=p[cnt]=num,sz[cnt]=1;return cnt;} int nw(int pre){ if(pre==0) return 0; cnt++,sum[cnt]=sum[pre],p[cnt]=p[pre],nd[cnt]=nd[pre],sz[cnt]=sz[pre]; c[cnt][0]=c[pre][0],c[cnt][1]=c[pre][1],rev[cnt]=rev[pre]; return cnt; } void pushdown(int x){ if(x==0||!rev[x]) return; int L=ls,R=rs; L=nw(L),R=nw(R),ls=L,rs=R; rev[ls]^=1,rev[rs]^=1; swap(ls,rs),rev[x]=0; } int merge(int x,int y){ if(x*y==0) return x|y; int now; if(nd[x]<nd[y]) now=nw(x),pushdown(now),c[now][1]=merge(c[now][1],y); else now=nw(y),pushdown(now),c[now][0]=merge(x,c[now][0]); pushup(now); return now; } Droot split(int x,int rk){ Droot tmp; if(x==0){tmp.A=tmp.B=0;return tmp;} x=nw(x),pushdown(x); if(sz[ls]>=rk) tmp=split(ls,rk),ls=tmp.B,pushup(x),tmp.B=x; else tmp=split(rs,rk-sz[ls]-1),rs=tmp.A,pushup(x),tmp.A=x; return tmp; } void reverse(int L,int R){ ti++,rt[ti]=nw(rt[at]),at=ti; Droot t1=split(rt[ti],L-1); Droot t2=split(t1.B,R-L+1); rev[t2.A]^=1; rt[ti]=merge(t1.A,t2.A); rt[ti]=merge(rt[ti],t2.B); } int query(int L,int R){ Droot t1=split(rt[at],L-1); Droot t2=split(t1.B,R-L+1); int numb=sum[t2.A]; rt[at]=merge(t1.A,t2.A),rt[at]=merge(rt[at],t2.B); return numb; } void ins(int num){int now=creat(num);rt[0]=merge(rt[0],now);} int build(int l,int r,int hp){ if(l>r) return 0; int x=creat(t[mid]); nd[x]=hp; ls=build(l,mid-1,hp+(rd()%200000)),rs=build(mid+1,r,hp+(rd()%200000)); pushup(x);return x; } int main(){ n=read(),T=read(),at=0; for(int i=1;i<=n;i++) t[i]=read(); rt[0]=build(1,n,rd()%200000); while(T--){ tpe=read(); if(tpe<3){ u=read(),v=read(); if(u>v) swap(u,v); if(tpe==1) reverse(u,v); else printf("%d\n",query(u,v)); } else at=read(); if(at>ti) break; } }