文艺平衡树(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;
}

最后:

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

全代码就不粘了。

posted @ 2018-09-05 14:47  LiGuanlin  阅读(219)  评论(6编辑  收藏  举报