zoukankan      html  css  js  c++  java
  • [bzoj4825] [loj#2018] [Hnoi2017] 单旋

    Description

    (H) 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构。伸展树((splay))是一种数据
    结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 (H) 国的必修技能。有一天,邪恶的“卡”带着
    他的邪恶的“常数”来企图毁灭 (H) 国。“卡”给 (H) 国的人洗脑说,(splay) 如果写成单旋的,将会更快。“卡”称
    “单旋 (splay) ”为“ (spaly) ”。虽说他说的很没道理,但还是有 (H) 国的人相信了,小 (H) 就是其中之一,(spaly)
    上成为他的信仰。 而 (H) 国的国王,自然不允许这样的风气蔓延,国王构造了一组数据,数据由 (m) 个操作构成,
    他知道这样的数据肯定打垮 (spaly),但是国王还有很多很多其他的事情要做,所以统计每个操作所需要的实际代价
    的任务就交给你啦。
    数据中的操作分为五种:

    1. 插入操作:向当前非空 (spaly) 中插入一个关键码为 (key) 的新孤立节点。插入方法为,先让 (key) 和根比较,如果
      (key) 比根小,则往左子树走,否则往右子树走,如此反复,直到某个时刻,(key) 比当前子树根 (x) 小,而 (x) 的左子
      树为空,那就让 (key) 成为 (x) 的左孩子; 或者 (key) 比当前子树根 (x) 大,而 (x) 的右子树为空,那就让 (key) 成为
      (x) 的右孩子。该操作的代价为:插入后,(key) 的深度。特别地,若树为空,则直接让新节点成为一个单个节点的树
      。(各节点关键码互不相等。对于“深度”的解释见末尾对 (spaly) 的描述)。
    2. 单旋最小值:将 (spaly) 中关键码最小的元素 (xmin) 单旋到根。操作代价为:单旋前 (xmin) 的深度。
      (对于单旋操作的解释见末尾对 (spaly) 的描述)。
    3. 单旋最大值:将 (spaly) 中关键码最大的元素 (xmax) 单旋到根。操作代价为:单旋前 (xmax) 的深度。
    4. 单旋删除最小值:先执行 2 号操作,然后把根删除。由于 2 号操作之后,根没有左子树,所以直接切断根和右子
      树的联系即可(具体见样例解释)。 操作代价同 2 号操 作。
    5. 单旋删除最大值:先执行 3 号操作,然后把根删除。 操作代价同 3 号操作。

    对于不是 (H) 国的人,你可能需要了解一些 (spaly) 的知识,才能完成国王的任务:
    (a).$ spaly$ 是一棵二叉树,满足对于任意一个节点 (x),它如果有左孩子 (lx),那么 (lx) 的关键码小于 (x) 的关键码。
    如果有右孩子 (rx),那么 (rx) 的关键码大于 (x) 的关键码。
    (b). 一个节点在 (spaly) 的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。
    (c). 单旋操作是对于一棵树上的节点 (x) 来说的。一开始,设 $ f$ 为 (x) 在树上的父亲。如果 (x)(f) 的左孩子,那么
    执行 (zig(x)) 操作(如上图中,左边的树经过 (zig(x)) 变为了右边的树),否则执行 (zag(x)) 操作(在上图中,将
    右边的树经过 (zag(f)) 就变成了左边的树)。每当执 行一次 (zig(x)) 或者 (zag(x))(x) 的深度减小 (1),如此反复,
    直到 (x) 为根。总之,单旋 (x) 就是通过反复执行 (zig)(zag)(x) 变为根。

    Input

    第一行单独一个正整数 (m)
    接下来 (m) 行,每行描述一个操作:首先是一个操作编号 (c in [1,5]),即问题描述中给出的五种操作中的编号,若 (c = 1),则再输入一个非负整数 (key),表示新插入节点的关键码。

    (1 leq m leq 10^5,1 leq key leq 10^9)
    所有出现的关键码互不相同。任何一个非插入操作,一定保证树非空。在未执行任何操作之前,树为空

    Output

    输出共 (m) 行,每行一个整数,第 (i) 行对应第 (i) 个输入的操作的代价。

    Sample Input

    5

    1 2

    1 1

    1 3

    4

    5

    Sample Output

    1

    2

    2

    2

    2


    想法

    首先发现,所有旋转上去的点要么最大要么最小,那么只会一直左旋或右旋
    之后手玩几组发现,转上去后,有变化的只是它的子节点接到了它的父节点上,原本的根节点接到了它下面,其他的相对顺序都没变
    那么这道题让我们维护的深度很好搞,就是它原本子节点的深度不变,它的深度变成1,其它点深度都+1

    而对于删除根节点,深度也很好维护,就是所有点深度-1

    接着考虑插入节点是怎么知道它应插到哪里。
    继续画一画发现,我们可以维护每个节点直接相连有几个可以插新节点的位置(0或1或2),假设原本树上有 (x) 个点比我们要插的点小,那么新点就应插在第 (x+1) 个空位上。它的深度就是它父节点深度+1,我们在找空位的时候就能知道它的父节点是谁。

    先把所有数据都进来,离散化一下。
    线段树维护深度和空位数和某一个区间内在树中的节点数。
    最大值/最小值怎么搞?可以用个 (set) 维护当前树中的点。

    嗯,口胡地差不多了,感觉不太难。
    写起来,啊啊啊啊啊怎么这么多细节啊啊啊啊啊!多多多多多多注意下。。。
    挺好也挺毒瘤的一道题。


    代码

    啊,191行的代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<set>
     
    using namespace std;
     
    int read(){
        int x=0;
        char ch=getchar();
        while(!isdigit(ch)) ch=getchar();
        while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
        return x;
    }
     
    const int N = 100005;
     
    int m,n;
    struct ope{ int op,x; }a[N];
    int b[N];
     
    set<int> s;
    int root,cnt,ch[N*2][2],sz[N*2],lazy[N*2],num[N*2];
    int pos[N];
    void build(int x,int l,int r){
        sz[x]=lazy[x]=num[x]=0;
        if(l==r) { pos[l]=x; return; }
        int mid=(l+r)>>1;
        build(ch[x][0]=++cnt,l,mid);
        build(ch[x][1]=++cnt,mid+1,r);
    }
    void pushdown(int x){
        if(lazy[x]==0) return ;
        lazy[ch[x][0]]+=lazy[x];
        lazy[ch[x][1]]+=lazy[x];
        lazy[x]=0;
    }
    void addsz(int x,int l,int r,int c,int d){
        sz[x]+=d;
        if(l==r) return;
        pushdown(x);
        int mid=(l+r)>>1;
        if(c<=mid) addsz(ch[x][0],l,mid,c,d);
        else addsz(ch[x][1],mid+1,r,c,d);
    }
    void addnum(int x,int l,int r,int c,int d){
        num[x]+=d;
        if(l==r) return;
        pushdown(x);
        int mid=(l+r)>>1;
        if(c<=mid) addnum(ch[x][0],l,mid,c,d);
        else addnum(ch[x][1],mid+1,r,c,d);
    }
    void adddep(int x,int l,int r,int L,int R,int c){
        if(L<=l && r<=R) { lazy[x]+=c; return; }
        pushdown(x);
        int mid=(l+r)>>1;
        if(L<=mid) adddep(ch[x][0],l,mid,L,R,c);
        if(R>mid) adddep(ch[x][1],mid+1,r,L,R,c);
    }
    void cgdep(int x,int l,int r,int c,int d){
        if(l==r) { lazy[x]=d; return; }
        pushdown(x);
        int mid=(l+r)>>1;
        if(c<=mid) cgdep(ch[x][0],l,mid,c,d);
        else cgdep(ch[x][1],mid+1,r,c,d);
    }
    int find(int x,int l,int r,int c){
        if(l==r) return l;
        pushdown(x);
        int mid=(l+r)>>1;
        if(sz[ch[x][0]]>=c) return find(ch[x][0],l,mid,c);
        return find(ch[x][1],mid+1,r,c-sz[ch[x][0]]);
    }
    int cal(int x,int l,int r,int c){
        if(l==r) return num[x];
        pushdown(x);
        int mid=(l+r)>>1;
        if(c<=mid) return cal(ch[x][0],l,mid,c);
        return num[ch[x][0]]+cal(ch[x][1],mid+1,r,c);
    }
    int dep(int x,int l,int r,int c){
        if(l==r) return lazy[x];
        pushdown(x);
        int mid=(l+r)>>1;
        if(c<=mid) return dep(ch[x][0],l,mid,c);
        return dep(ch[x][1],mid+1,r,c);
    }
     
    int rt,fa[N],son[N][2];
     
    void mkrt_min(int x){
        if(x+1<=fa[x]-1) adddep(root,1,n,x+1,fa[x]-1,-1);
        adddep(root,1,n,1,n,1);
        cgdep(root,1,n,x,1);
        son[fa[x]][0]=son[x][1];
        if(son[x][1]) fa[son[x][1]]=fa[x];
        else addsz(root,1,n,fa[x],1),addsz(root,1,n,x,-1);
        fa[rt]=x; son[x][1]=rt; rt=x; fa[x]=0;
    }
    void mkrt_max(int x){
        if(fa[x]+1<=x-1) adddep(root,1,n,fa[x]+1,x-1,-1);
        adddep(root,1,n,1,n,1);
        cgdep(root,1,n,x,1);
        son[fa[x]][1]=son[x][0];
        if(son[x][0]) fa[son[x][0]]=fa[x];
        else addsz(root,1,n,fa[x],1),addsz(root,1,n,x,-1);
        fa[rt]=x; son[x][0]=rt; rt=x; fa[x]=0;
    }
     
    int main()
    {
        m=read();
        for(int i=0;i<m;i++){
            a[i].op=read();
            a[i].x= (a[i].op==1) ? read() :  0 ;
            if(a[i].x) b[++n]=a[i].x;
        }
        sort(b+1,b+1+n);
        n=unique(b+1,b+1+n)-b-1;
         
        build(root=++cnt,1,n);
        for(int i=0;i<m;i++){
            if(a[i].op==1){
                int x=lower_bound(b+1,b+1+n,a[i].x)-b;
                if(s.size()==0){
                    s.insert(x);
                    rt=x;
                    addsz(root,1,n,x,2);
                    cgdep(root,1,n,x,1);
                    addnum(root,1,n,x,1);
                    printf("1
    ");
                }
                else{
                    s.insert(x);
                    int y=find(root,1,n,cal(root,1,n,x)+1),d=dep(root,1,n,y)+1;
                    fa[x]=y; 
                    son[fa[x]][x>fa[x]]=x;
                    addsz(root,1,n,fa[x],-1);
                    addsz(root,1,n,x,2);
                    cgdep(root,1,n,x,d);
                    addnum(root,1,n,x,1);
                    printf("%d
    ",d);
                }
            }
            else if(a[i].op==2){
                int x=*s.begin();
                if(rt==x) printf("1
    ");
                else{
                    printf("%d
    ",dep(root,1,n,x));
                    mkrt_min(x);
                }
            }
            else if(a[i].op==3){
                int x=*(--s.end());
                if(rt==x) printf("1
    ");
                else{
                    printf("%d
    ",dep(root,1,n,x));
                    mkrt_max(x);
                }
            }
            else if(a[i].op==4){
                int x=*s.begin();
                if(rt==x) printf("1
    ");
                else{
                    printf("%d
    ",dep(root,1,n,x));
                    mkrt_min(x);
                }
                adddep(root,1,n,1,n,-1);
                addsz(root,1,n,x,-sz[pos[x]]);
                addnum(root,1,n,x,-1);
                rt=son[x][1]; son[x][1]=0; fa[rt]=0;
                s.erase(x);
            }
            else{
                int x=*(--s.end());
                if(rt==x) printf("1
    ");
                else{
                    printf("%d
    ",dep(root,1,n,x));
                    mkrt_max(x);
                }
                adddep(root,1,n,1,n,-1);
                addsz(root,1,n,x,-sz[pos[x]]); /**/
                addnum(root,1,n,x,-1);
                rt=son[x][0]; son[x][0]=0; fa[rt]=0;
                s.erase(x);
            }
        }
         
        return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    【Others】2048逻辑整理
    【MySQL】MySQL学习笔记(一)
    【PHP】对TP中session的理解
    【Python基础】python 作用域
    【phantomJs + selenium】动态cookie,数据采集记录
    [ZJOI2015][LOJ2137]诸神眷顾的幻想乡(广义SAM)
    [CF235C]Cyclical Quest(SAM)
    [BZOJ1426]收集邮票(概率期望dp)
    [HDU5421]Victor and String(PAM)
    [CF932G]Palindrome Partition(PAM回文划分dp)
  • 原文地址:https://www.cnblogs.com/lindalee/p/11432192.html
Copyright © 2011-2022 走看看