!前置技能&概念!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; } }