• 关于Splay的几个灵魂问题


    关于Splay的几个灵魂问题

    写着一篇文章的目的在于,我被几道带有lazy标记的题折磨了很久。

    1. Splay是在维护节点值的顺序吗?

    我们先来看一看常规的splay定义:

    const int N = 1e5+5;
    
    struct Node{
      int s[2]; 	// 左右儿子
      int p;			// 父节点
      int v;			// 节点值
      int size;		// 子树大小
    }tr[N];
    

    请问,splay是在维护节点的信息v吗?要回答这个问题,我们应该看看影响splay平衡性的两个rotate,splay

    int ws(int x){
      return tr[tr[x].p].s[1] == x;
    }
    
    void push_up(int x){
    	tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
    }
    
    void rotate(int x){
      int y = tr[x].p;
      int z = tr[y].p;
      int k = ws(x);
    	tr[z].s[ws(y)] = x;
      tr[y].p = x;
      tr[y].s[k] = tr[x].s[k^1];
      tr[tr[x].s[k^1]].p = y;
      tr[x].p = z;
      tr[x].s[k^1] = y;
      push_up(y);
      push_up(x);
    }
    
    void splay(int x, int k){
      while(tr[x].p != k){
    		int y = tr[x].p;
        int z = tr[y].p;
        if(z != k){
          if(ws(x) ^ ws(y)){
            rotate(x);
          }else{
            rotate(y);
          }
        }
        rotate(x);
      }
      if(!k) root = x;
    }
    

    可以看出,splay在旋转时并不会像其他平衡树(如AVL)一样对节点值进行比较。事实上,splay并不会维护节点值的大小顺序,而是在维护整棵树的中序遍历不变。

    2. Splay的lazy什么时候需要push_down?

    push_down会影响该节点的子树的信息。一方面是在需要访问子树信息时push_down,这个自然不必多提;另一方面,如果push_down操作会改变树的结构,如交换左右子树,而查询操作要求获得某一侧子树的大小,或判断该节点是哪一个儿子(这个很常见),则必须先push_down

    我们以flip_lazy为例,给出两种风格的lazy,请选择合适的一种使用:

    • push_down不改变本节点信息

      void push_down(int x){
          if(tr[x].lazy){
              if(tr[x].s[0]){
                  swap(tr[tr[x].s[0]].s[0], tr[tr[x].s[0]].s[1]);
                  tr[tr[x].s[0]].lazy ^= 1;
              }
              if(tr[x].s[1]){
                  swap(tr[tr[x].s[1]].s[0], tr[tr[x].s[1]].s[1]);
                  tr[tr[x].s[1]].lazy ^= 1;
              }
              tr[x].lazy = 0;
          }
      }
      
      void set_lazy(int x){
        	tr[x].lazy ^= 1;
        	swap(tr[x].s[0], tr[x].s[1]);
      }
      
    • push_down改变本节点信息

      void push_down(int x){
          if(tr[x].lazy){
              swap(tr[x].s[0], tr[x].s[1]);
              if(tr[x].s[0]) tr[tr[x].s[0]].lazy ^= 1;
              if(tr[x].s[1]) tr[tr[x].s[1]].lazy ^= 1;
              tr[x].lazy = 0;
          }
      }
      
      void set_lazy(int x){
      		tr[x].lazy ^= 1;
      }
      

    此外,rotatesplaygetkget_rank操作都需要push_down

    void rotate(int x){
        int y = tr[x].p;
        int z = tr[y].p;
        
    
        push_down(y);
        push_down(x);
        
        int k = ws(x);
        
        tr[z].s[ws(y)] = x;
        tr[y].p = x;
        tr[y].s[k] = tr[x].s[k^1];
        tr[tr[x].s[k^1]].p = y;
        tr[x].p = z;
        tr[x].s[k^1] = y;
        push_up(y);
        push_up(x);
    }
    
    void splay(int x, int k){
        while (tr[x].p != k) {
            int y = tr[x].p;
            int z = tr[y].p;
            push_down(z);
            push_down(y);
            push_down(x);
            if(z != k){
                if(ws(x) ^ ws(y)){
                    rotate(x);
                }else{
                    rotate(y);
                }
            }
            rotate(x);
        }
        if(!k) rt = x;
    }
    
    int build(int l, int r, int p){
        if(l > r) return 0;
        else if(l == r){
            tr[l].lazy = 0;
            tr[l].p = p;
            tr[l].v = arr[l];
            tr[l].s[0] = tr[l].s[1] = 0;
            tr[l].size = 1;
            return l;
        }else{
            int mid = (l+r)>>1;
            tr[mid].lazy = 0;
            tr[mid].p = p;
            tr[mid].v = arr[mid];
            tr[mid].s[0] = build(l, mid-1, mid);
            tr[mid].s[1] = build(mid+1, r, mid);
            
            push_up(mid);
            return mid;
        }
    }
    
    int getk(int k){
        int u = rt;
        while(u){
            push_down(u);
            if(tr[tr[u].s[0]].size >= k){
                u = tr[u].s[0];
            }else if(tr[tr[u].s[0]].size + 1 == k){
                return u;
            }else{
                k -= tr[tr[u].s[0]].size + 1;
                u = tr[u].s[1];
            }
        }
        return -1;
    }
    
    int get_rank(int u){
        splay(u, 0);
        push_down(u);
        return tr[tr[u].s[0]].size;
    }
    
    ---- suffer now and live the rest of your life as a champion ----
  • 相关阅读:
    kubernetes(十九) Ceph存储入门
    Kubernetes 使用 Weave Scope 监控集群(十七)
    在Mac下安装Wordpress
    关闭 Mac 上的虚拟内存
    Underlay、Overlay、大二层介绍
    Docker For Mac 下安装 Rancher
    Minikube-Kubernetes本地环境进行开发
    minikube---kubectl常用命令
    Mac安装minikube
    java内存模型
  • 原文地址:https://www.cnblogs.com/popodynasty/p/14527742.html
Copyright © 2020-2023  润新知