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;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    POJ 1811 Prime Test 素性测试 分解素因子
    sysbench的安装与使用
    电脑中已有VS2005和VS2010安装.NET3.5失败的解决方案
    I.MX6 show battery states in commandLine
    RPi 2B Raspbian system install
    I.MX6 bq27441 driver porting
    I.MX6 隐藏电池图标
    I.MX6 Power off register hacking
    I.MX6 Goodix GT9xx touchscreen driver porting
    busybox filesystem httpd php-5.5.31 sqlite3 webserver
  • 原文地址:https://www.cnblogs.com/lindalee/p/11432192.html
Copyright © 2011-2022 走看看