后缀平衡树-学习笔记

i207M

2019-05-03 10:43:36

Personal

~~很高兴后缀平衡树的思想之前在某次Rush题的时候想出来了~~ 首先我个人的习惯是,每次在结尾插入一个字符,然后把前缀排序,字典序比较为从后往前。 其实想法很简单,当我们插入一个新的前缀时,它和其他前缀的字典序比较:如果首字母不同,直接比较;否则,它们去掉首字母后的两个前缀的字典序比较关系已经确定了,我们可以直接查。 具体实现,如果可以$O(\log ^2n)$,我们可以 1.在平衡树上求出rk 2.Hash+二分求LCS 更快的方法:对每个点维护double权值表示自己的排名。 每个点有一个权值区间,表示自己子树里的权值都在这个区间里,自己选择区间的中点。 这就要求树高不能太高,所以需要平衡树。 而且树的结构一旦变化,pushup的时间是$O(size)$的,于是普通的平衡树就用不了了。 我们可以 1.用Treap,期望子树大小$O(\log n)$ 2.用替罪羊树,暴力重构。 我选择后者。 人傻常数大,慢的一批。BZOJ 5084 好像SGT的alpha一般要设的比较大,0.8左右。 ```cpp #define N 200005 #define ls (ch[x][0]) #define rs (ch[x][1]) const ull bas=233; ull h[N][17],pw[N]; int ys[N]; namespace SGT { const double alpha=0.85; double rk[N]; int tot,pos[N],sz[N],ch[N][2]; il void up(int x) {sz[x]=sz[ls]+1+sz[rs];} pair<int,int> st[N]; int tp; void dfs(int x) { if(!x) return; dfs(ls); st[++tp]=mp(pos[x],x); dfs(rs); } void build(int &x,int l,int r,const double &vl,const double &vr) { if(l>r) {x=0; return;} gm; x=st[mid].se,ys[pos[x]=st[mid].fi]=x; rk[x]=(vl+vr)/2; build(ls,l,mid-1,vl,rk[x]); build(rs,mid+1,r,rk[x],vr); up(x); } double nl,nr; il void rebuild(int &x) { tp=0; dfs(x); build(x,1,tp,nl,nr); } int *ned; #define chk(x) {if(max(sz[ls],sz[rs])>sz[x]*alpha) ned=&x,nl=vl,nr=vr;} il bool les(int x,int y) { return h[x][0]<h[y][0]||(h[x][0]==h[y][0]&&rk[ys[x-1]]<rk[ys[y-1]]); } void ins(int &x,int p,const double &vl,const double &vr) { if(!x) { x=++tot; ys[pos[tot]=p]=x,rk[x]=(vl+vr)/2,sz[x]=1; return; } if(les(p,pos[x])) ins(ls,p,vl,rk[x]); else ins(rs,p,rk[x],vr); up(x); chk(x); } int rt; il void ins(int p) { ned=NULL; ins(rt,p,0,1); if(ned!=NULL) rebuild(*ned); } int merge(int x,int y) { if(!x||!y) return x|y; if(sz[x]>sz[y]) return rs=merge(rs,y),up(x),x; else return ch[y][0]=merge(x,ch[y][0]),up(y),y; } void del(int &x,int p) { if(pos[x]==p) { x=merge(ls,rs); return; } if(les(p,pos[x])) del(ls,p); else del(rs,p); --sz[x]; } il void del(int p) {del(rt,p);} int getrk(int x,int p) { if(pos[x]==p) return sz[ls]+1; if(les(p,pos[x])) return getrk(ls,p); else return getrk(rs,p)+sz[ls]+1; } int getp(int x,int k) { if(!x||k<=0) return -1; if(k<=sz[ls]) return getp(ls,k); if(k==sz[ls]+1) return pos[x]; return getp(rs,k-sz[ls]-1); } il int getrk(int p) {return getrk(rt,p);} il int getp(int k) {return getp(rt,k);} } LL ans; il int lcs(int x,int y) { ull hx=0,hy=0; int ml=0; for(ri i=16; i>=0; --i) { if((1<<i)>min(x,y)) continue; ull nx=h[x][i]*pw[ml]+hx, ny=h[y][i]*pw[ml]+hy; if(nx==ny) { hx=nx,hy=ny; x-=1<<i,y-=1<<i,ml+=1<<i; } } return ml; } int cnt; void insert(const char c) { h[++cnt][0]=c; for(ri i=1; i<=16&&cnt-(1<<(i-1))>0; ++i) h[cnt][i]=h[cnt-(1<<(i-1))][i-1]*pw[1<<(i-1)]+h[cnt][i-1]; SGT::ins(cnt); int k=SGT::getrk(cnt),a=SGT::getp(k-1),b=SGT::getp(k+1); if(a!=-1&&b!=-1) ans-=lcs(a,b); if(a!=-1) ans+=lcs(a,cnt); if(b!=-1) ans+=lcs(b,cnt); } void remove() { int k=SGT::getrk(cnt),a=SGT::getp(k-1),b=SGT::getp(k+1); SGT::del(cnt); if(a!=-1) ans-=lcs(a,cnt); if(b!=-1) ans-=lcs(b,cnt); if(a!=-1&&b!=-1) ans+=lcs(a,b); --cnt; } signed main() { #ifdef M207 freopen("in.in","r",stdin); // freopen("ot.out","w",stdout); #endif pw[0]=1; for(ri i=1; i<N; ++i) pw[i]=pw[i-1]*bas; static char tc[N]; scanf("%s",tc+1); int cq=strlen(tc+1); for(ri i=1; i<=cq; ++i) { if(tc[i]=='-') remove(); else insert(tc[i]); out((LL)cnt*(cnt+1)/2-ans); } return 0; } ```