[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\n",D);
        }
        if(!(op&1))
        {
            int x=Find(rt,2),y=min(FindLeft(rt,dep[x]),size[rt]-1)-1;
            printf("%d\n",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\n",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;
}
posted @ 2017-05-17 00:37  FallDream  阅读(613)  评论(0编辑  收藏