zoukankan      html  css  js  c++  java
  • 文艺平衡树(splay模板)

    题干:splay模板,要求维护区间反转。

    splay是一种码量小于treap,但支持排名,前驱后继等treap可求的东西,也支持区间反转的平衡树。

    但是有两个坏处:

    1.splay常数远远大于treap以及stl中的set。

    2.没有可持久化splay,但有可持久化treap。

    下面是代码:

    1.pushup以及pushdown

    pushup用于维护某点所在子树大小。

    void pushup(int u)
    {
        tr[u].siz = tr[tr[u].ch[0]].siz + tr[tr[u].ch[1]].siz + 1;
    }

    一行。

    pushdown用于标记下推。

    void pushdown(int u)
    {
        if(tr[u].mrk)
        {
            tr[u].mrk=0;
            swap(tr[u].ch[0],tr[u].ch[1]);
            tr[tr[u].ch[0]].mrk^=1;
            tr[tr[u].ch[1]].mrk^=1;
        }
    }

    2.rotate

    rotate相当于treap中的lturn和rturn,是将x点转到他的父节点处。

    void rotate(int x)
    {
        int y = tr[x].fa;
        int z = tr[y].fa;
        int k = (tr[y].ch[1]==x);
        tr[x].fa = z,tr[z].ch[tr[z].ch[1]==y]=x;
        tr[y].ch[k] = tr[x].ch[k^1],tr[tr[x].ch[k^1]].fa = y;
        tr[y].fa = x,tr[x].ch[k^1]=y;
        pushup(y),pushup(x);
    }

    注意改变父子关系时的顺序问题,还有两个pushup,应该先pushup儿子后pushup父亲。

    3.splay(splay核心操作)

    splay(a,b),将a旋转到b的儿子处。

    下面是双旋splay:

    void splay(int x,int goal)
    {
        while(tr[x].fa!=goal)
        {
            int y = tr[x].fa;
            int z = tr[y].fa;
            if(z!=goal)
                ((tr[z].ch[1]==y)^(tr[y].ch[1]==x))?rotate(x):rotate(y);
            rotate(x);
        }
        if(goal==0)rot=x;
    }

    代码中while中的if,机房普遍认为是为了维护整棵树的深度。

    4.insert

    码量比treap少很多:

    void insert(int x)
    {
        int u = rot,fa;
        while(u)fa=u,u=tr[u].ch[tr[u].v<x];
        u=++tot;
        if(fa)tr[fa].ch[x>tr[fa].v]=u;
        tr[u].fa = fa,tr[u].v = x,tr[u].siz = 1;
        splay(u,0);
    }

    5.查询排名

    代码:

    int query_kth(int k)
    {
        int u = rot;
        while(1)
        {
            pushdown(u);
            int t = tr[tr[u].ch[0]].siz;
            if(t==k-1)return u;
            else if(t>=k)u=tr[u].ch[0];
            else k-=t+1,u=tr[u].ch[1];
        }
    }

    6.区间旋转

    先查询前驱后继,然后把所在子树夹到前驱后继之间,一个标记解决问题。

    void chg(int l,int r)
    {
        l = query_kth(l);//前驱
        r = query_kth(r+2);//后继
        splay(l,0);splay(r,l);
        tr[tr[tr[rot].ch[1]].ch[0]].mrk^=1;
    }

    最后:

    推极大极小,推极大极小,推极大极小!!!

    全代码就不粘了。

  • 相关阅读:
    BZOJ 3196 二逼平衡树
    BZOJ 4241 历史研究
    Problem 71:Ordered fractions
    矿工安全生产
    Codeforces 771C:Bear and Tree Jumps
    Problem 77:Prime summations
    Problem 69:Totient maximum
    关于Euclid算法
    团体程序设计天梯赛-练习集
    埃蒙的时空航道
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/9592046.html
Copyright © 2011-2022 走看看