• Splay学习笔记


    n(n<=500000)个数,要求维护区间加,区间查询

    很简单,用线段树/树状数组随便写写就能过

    n(n<=500000)个数,维护插入、删除、找区间第K大/小

    用平衡树/set也能过

    n(n<=500000)个数,维护区间翻转,区间查询,插入,删除

    这个时候就需要用到伸展树(Splay)

    splay是一种极(常)灵(数)活(大)的数据结构,它能够维护线段树的部分操作和平衡树的操作
    它的美妙之处在于能够旋转,从而完成一系列操作
    它的旋转和平衡树的只转一次不同,它可以进行双旋操作来保证较优秀的时间复杂度(但常数还是大)

    双旋操作是基于单旋的
    首先是单旋:

    和平衡树相同

    然后是双旋:

    当p不是根节点,且x和p同为左孩子或右孩子时进行Zig-Zig操作。
    当x和p同为左孩子时,依次将p和x右旋;
    当x和p同为右孩子时,依次将p和x左旋。

    当p不是根节点,且x和p不同为左孩子或右孩子时,进行Zig-Zag操作。
    当p为左孩子,x为右孩子时,将x左旋后再右旋。
    当p为右孩子,x为左孩子时,将x右旋后再左旋。

    有了单旋和双旋就可以进行splay操作:

    int get(int o){return tree[tree[o].fa].son[1]==o;}
    void rotate(QAQ o){
        int fa=tree[o].fa,ff=tree[fa].fa;
        int pd=get(o);
        if(ff) tree[ff].son[get(fa)]=o;
        tree[o].fa=ff;
        tree[fa].son[pd]=tree[o].son[1-pd];
        tree[tree[o].son[1-pd]].fa=fa;
        tree[fa].fa=o;tree[o].son[1-pd]=fa;
        push_up(fa);push_up(o);
    }
    
    void splay(int o,int goal){
    //goal是目标节点的父节点,为防止旋转之后导致节点更换带来的影响
        while(tree[o].fa!=goal){
            int fa=tree[o].fa,ff=tree[fa].fa;
            if(ff!=goal) if(tree[ff].son[1]==fa ^ tree[fa].son[1]==o) rotate(o);
                        else rotate(fa);
            rotate(o);
        }
        if(!goal) rot=o;//换根
        push_up(o);
    }
    

    进行区间操作[l,r]时,常常把节点l-1转到根,r+1转到根的右儿子,这样根的右儿子的左儿子就是操作区间 ,并且常常添加虚拟节点0和n+1来进行[1,n]的操作
    区间翻转就直接交换左右儿子
    区间加直接加lazy标记

    还能查询某一个值的排名:找到节点,转到根,根的做儿子大小+1就是排名

    注意:splay中有些操作不能同时进行,例如求第K大和区间操作就不能同时进行,因为前一种操作改变了原序列顺序,后一个操作要求不能改变,这样就发生冲突。splay旋转仅仅是改变树的形状,并没有改变原序列顺序(树的中序遍历)

  • 相关阅读:
    让你的python程序同时兼容python2和python3
    Python3.x 和Python2.x 区别
    python类型转换、数值操作(收藏)
    Python IDLE快捷键一览
    Python数据类型详解
    sublime text3安装SublimeREPL--解决不能运行input()的问题
    C语言(六)语句
    C语言(五)数学函数
    C语言(四)隐式类型转换规则
    C语言(三)关键字
  • 原文地址:https://www.cnblogs.com/heower/p/8463547.html
Copyright © 2020-2023  润新知