关于平衡线段树的一点研究

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于平衡线段树的一点研究相关的知识,希望对你有一定的参考价值。

平衡树可以维护带插入删除的序列操作,加上一些线段树的特性即可像线段树一样维护区间信息

同样,线段树可以维护区间信息,加上一些平衡树的特性也就可以实现插入删除

不知为何似乎没见过什么相关资料所以就自己yy了一下

考虑旋转:

技术分享

可以发现每次旋转只需更新一个区间的信息

定义每个节点的size为子树中叶节点个数

考虑类似sbt的平衡规则,每个节点是平衡的,当且仅当其子节点的size不小于另一子节点的任一子节点的size

类似线段树,每个叶节点维护原序列,非叶结点维护区间

对于在第x个元素前插入元素,首先找到代表x的叶节点,将x换为一棵size=2的子树,其中右子节点为原来的x,左子节点为新插入元素

修改操作和区间查询类似线段树

删除操作很简单,因为删的是叶节点,可以将待删除节点和其父节点删去,兄弟节点补到原位置

每次插入/删除后要沿操作路径调整平衡以保证单次操作严格O(logn)

在以上几个操作中平衡线段树的常数明显小于splay,查询操作的常数与线段树没有明显差别,并且必要时可以支持严格复杂度的可持久化

当然类似替罪羊树/treap的平衡规则也是可以用的,但复杂度变为均摊/期望

splay的旋转规则不易推广至平衡线段树

于是有了裸题 bzoj1014 [JSOI2008]火星人prefix

技术分享
#include<cstdio>
#include<cstring>
typedef unsigned long long u64;
const int N=600050,P=53;
int lc[N],rc[N],sz[N],p=1;
u64 v[N],pp[N];
char str[N],op[64],ch[64];
inline void up(int w){
    v[w]=v[lc[w]]*pp[sz[rc[w]]]+v[rc[w]];
}
inline void lrot(int&w){
    int u=lc[w];
    lc[w]=rc[u];
    rc[u]=w;
    v[u]=v[w];
    sz[u]=sz[w];
    up(w);
    sz[w]=sz[lc[w]]+sz[rc[w]];
    w=u;
}
inline void rrot(int&w){
    int u=rc[w];
    rc[w]=lc[u];
    lc[u]=w;
    v[u]=v[w];
    sz[u]=sz[w];
    up(w);
    sz[w]=sz[lc[w]]+sz[rc[w]];
    w=u;
}
inline void chk_l(int&w){
    int m=sz[rc[w]],l=sz[lc[lc[w]]],r=sz[rc[lc[w]]];
    if(m<l)lrot(w);
    else if(m<r)rrot(lc[w]),lrot(w);
}
inline void chk_r(int&w){
    int m=sz[lc[w]],l=sz[lc[rc[w]]],r=sz[rc[rc[w]]];
    if(m<r)rrot(w);
    else if(m<l)lrot(rc[w]),rrot(w);
}
void ins(int&w,int k){
    ++sz[w];
    if(sz[w]==2){
        lc[w]=++p;
        v[p]=ch[0]-a+1;
        sz[p]=1;
        rc[w]=++p;
        v[p]=v[w];
        sz[p]=1;
        up(w);
    }else if(k<sz[lc[w]]){
        ins(lc[w],k);
        up(w);
        chk_l(w);
    }else{
        ins(rc[w],k-sz[lc[w]]);
        up(w);
        chk_r(w);
    }
}
void chg(int&w,int k){
    if(sz[w]==1){
        v[w]=ch[0]-a+1;
        return;
    }
    if(k<=sz[lc[w]])chg(lc[w],k);
    else chg(rc[w],k-sz[lc[w]]);
    up(w);
}
int l,r;
u64 ans=0;
void get(int w,int L,int R){
    if(l<=L&&R<=r){
        ans=ans*pp[sz[w]]+v[w];
        return;
    }
    int M=L+sz[lc[w]];
    if(l<M)get(lc[w],L,M-1);
    if(r>=M)get(rc[w],M,R);
}
int build(int L,int R){
    int w=++p;
    if(L==R){
        v[w]=str[L]-a+1;
        sz[w]=1;
        return w;
    }
    int M=L+R>>1;
    sz[w]+=sz[lc[w]=build(L,M)];
    sz[w]+=sz[rc[w]=build(M+1,R)];
    up(w);
    return w;
}
inline int input(){
    int x=0,c=getchar();
    while(c>57||c<48)c=getchar();
    while(c>47&&c<58)x=x*10+c-48,c=getchar();
    return x;
}
inline int getc(){
    int c=getchar();
    while(c<=32)c=getchar();
    return c;
}
int main(){
    pp[0]=1;
    for(int i=1;i<100050;i++)pp[i]=pp[i-1]*P;
    scanf("%s",str+1);
    int n=strlen(str+1)+1,q,x,y;
    int rt=build(1,n);
    q=input();
    while(q--){
        op[0]=getc();
        if(op[0]==Q){
            x=input();y=input();
            ans=0;
            int L=0,R=n-(x>y?x:y);
            while(L<R){
                int M=L+R+1>>1;
                ans=0;
                l=x,r=x+M-1;
                if(l<=r)get(rt,1,n);
                u64 a1=ans;
                ans=0;
                l=y,r=y+M-1;
                if(l<=r)get(rt,1,n);
                if(a1!=ans)R=M-1;
                else L=M;
            }
            printf("%d\\n",L);
        }else if(op[0]==R){
            x=input();ch[0]=getc();
            chg(rt,x);
        }else{
            x=input();ch[0]=getc();
            ins(rt,x);
            ++n;
        }
    }
    return 0;
}
显示代码

 

以上是关于关于平衡线段树的一点研究的主要内容,如果未能解决你的问题,请参考以下文章

acm常见算法考点_线段树

模板二逼平衡树(树套树)

[除一波线段树和平衡树的草]

bzoj4552/Tjoi2016&Heoi2016排序——二分+线段树/平衡树+线段树分裂与合并

静态区间第k大 树套树解法

12.6日记