• [bzoj4825]:[Hnoi2017]单旋


    来自FallDream的博客,未经允许,请勿转载,谢谢。

    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 变为根
    m<=10^5
     
     
    很牛逼的一道题233 
    只有傻瓜才会写一个spaly去模拟这些操作。
    因为这道题只对最值进行操作,所以每次操作全是左旋或者全是右旋
    发现把最小值转上去的时候,它的右子树深度不变,其他点深度+1,然后最小值深度变为1
    转最大值同理
    插入的时候只可能插在前驱右儿子或者后继左儿子。实际上它们只有一个位置是空的,判断插入到哪个即可。
    这个明显可以维护。只需要支持区间加,求出最长的大于某一深度的区间,插入一个点,查询前驱后继等操作即可。
    因为本题可以离线,所以直接线段树就好了。
    当然,既然是一道关于spaly的题,你也可以用splay直接维护哦  复杂度nlogn
    #include<iostream>
    #include<cstdio>
    #define getchar() (*S++)
    char B[1<<26],*S=B;
    #define INF 2000000000
    #define MN 100000
    using namespace std;
    inline int read()
    {
        int x = 0; char ch = getchar();
        while(ch < '0' || ch > '9')ch = getchar();
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x;
    }
    
    int n,fa[MN+5],val[MN+5],cnt=0,c[MN+5][2],rt=0,Q,s[MN+5],dep[MN+5],mn[MN+5],size[MN+5];
    
    inline void pushdown(int x)
    {
        int l=c[x][0],r=c[x][1];
        val[l]+=val[x];dep[l]+=val[x];mn[l]+=val[x];
        val[r]+=val[x];dep[r]+=val[x];mn[r]+=val[x];
        val[x]=0;
    }
    
    inline void update(int x)
    {
        int l=c[x][0],r=c[x][1];
        size[x]=size[l]+size[r]+1;
        mn[x]=dep[x];
        if(l) mn[x]=min(mn[x],mn[l]);
        if(r) mn[x]=min(mn[x],mn[r]);
    }
    
    void rotate(int x,int&k)
    {
        int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
        if(y==k) k=x; else c[z][c[z][1]==y]=x;
        fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
        c[y][l]=c[x][r];c[x][r]=y;
        update(y);update(x);
    }
    
    void splay(int x,int&k)
    {
        for(;x!=k;rotate(x,k))
            if(fa[x]!=k) rotate((c[fa[fa[x]]][1]==fa[x]^c[fa[x]][1]==x)?x:fa[x],k);
    }
    
    void ins(int&x,int S,int d,int last)
    {
        if(!x)
        {
            x=++cnt;s[cnt]=S;dep[cnt]=mn[cnt]=d;
            size[cnt]=1;fa[cnt]=last;return;
        }
        ins(c[x][S>s[x]],S,d,x);
        update(x);
    }
    
    int Find_Before(int x,int S)
    {
        if(!x) return 0;
        if(val[x]) pushdown(x);
        if(s[x]>S) return Find_Before(c[x][0],S);
        return (Q=Find_Before(c[x][1],S))?Q:x;
    }
    
    int Find_After(int x,int S)
    {
        if(!x) return 0;
        if(val[x]) pushdown(x);
        if(s[x]<S) return Find_After(c[x][1],S);
        return (Q=Find_After(c[x][0],S))?Q:x;
    }
    
    int Find(int x,int rk)
    {
        if(val[x]) pushdown(x);
        int sz=size[c[x][0]]+1;
        if(sz==rk) return x;
        if(sz<rk) return Find(c[x][1],rk-sz);
        return Find(c[x][0],rk);
    }
    
    int FindLeft(int x,int d)
    {
        if(!x) return 0;
        if(val[x]) pushdown(x);
        if(min(mn[c[x][0]],dep[x])>=d) return FindLeft(c[x][1],d)+size[c[x][0]]+1;
        else return FindLeft(c[x][0],d);
    }
    
    int FindRight(int x,int d)
    {
        if(!x) return 0;
        if(val[x]) pushdown(x);
        if(min(mn[c[x][1]],dep[x])>=d) return FindRight(c[x][0],d)+size[c[x][1]]+1;
        else return FindRight(c[x][1],d);
    }
    
    inline int Split(int l,int r)
    {
        int Lt=Find(rt,l-1),Rt=Find(rt,r+1);
        splay(Lt,rt);splay(Rt,c[rt][1]);
        return c[c[rt][1]][0];
    }
    
    inline void Modify(int l,int r,int ad)
    {
        int y=Split(l,r);
        val[y]+=ad;mn[y]+=ad;dep[y]+=ad;
    }
    
    void change(int x,int S)
    {
        if(val[x]) pushdown(x);
        if(s[x]==S) dep[x]=1;
        else change(c[x][S>s[x]],S);
        update(x);
    }
    
    int main()
    {
        fread(B,1,1<<26,stdin);
        n=read();ins(rt,-INF,INF,0);ins(rt,INF,INF,0);mn[0]=INF;
        for(int i=1;i<=n;++i)
        {
            int op=read();
            if(op==1)
            {
                int x=read(),bef=Find_Before(rt,x),aft=Find_After(rt,x);
                int D=max(bef>2?dep[bef]:0,aft>2?dep[aft]:0)+1;
                ins(rt,x,D,0);splay(cnt,rt);
                printf("%d
    ",D);
            }
            if(!(op&1))
            {
                int x=Find(rt,2),y=min(FindLeft(rt,dep[x]),size[rt]-1)-1;
                printf("%d
    ",dep[x]);
                Modify(2,size[rt]-1,1);
                if(y>1) Modify(2,y+1,-1);
                change(rt,s[x]);
            }
            if((op&1)&&op>1)
            {
                int x=Find(rt,size[rt]-1),y=min(FindRight(rt,dep[x]),size[rt]-1)-1;
                printf("%d
    ",dep[x]);
                Modify(2,size[rt]-1,1);
                if(y>1) Modify(size[rt]-y,size[rt]-1,-1);
                change(rt,s[x]);
            }
            if(op>=4)
            {
                if(op==4) splay(Find(rt,2),rt);
                else splay(Find(rt,size[rt]-1),rt);
                int l=(op==5),r=l^1,y=c[rt][l];
                c[y][r]=c[rt][r];fa[y]=0;
                fa[c[rt][r]]=y;rt=y;
                val[rt]-=1;update(rt);
            }
        }
        return 0;
    }
  • 相关阅读:
    NodeJS学习笔记 进阶 (11)Nodejs 进阶:调试日志打印:debug模块
    NodeJS学习笔记 进阶 (10)Nodejs 进阶:log4js入门实例(ok))
    NodeJS学习笔记 进阶 (9)express+cookie-parser:签名机制深入剖析(ok)
    NodeJS学习笔记 进阶 (8)express+morgan实现日志记录(ok)
    NodeJS学习笔记 进阶 (7)express+session实现简易身份认证(ok)
    NodeJS学习笔记 进阶 (6)本地调试远程服务器上的Node代码(ok)
    NodeJS学习笔记 进阶 (5)将图片转成datauri嵌入到html(ok)
    51nod 1287 线段树
    51nod 1043 数位dp
    51nod 1042 数位dp
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj4825.html
Copyright © 2020-2023  润新知