【jzoj5072】【GDOI2017第三轮模拟day1】【单旋】【数据结构】

题目大意

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

解题思路

将点离散化,用数组维护father和son[0,1],用线段树维护dep。考虑到插入只会插入在权值相邻的两个节点,用set维护直接插入。
考虑旋到根,自己的深度变为1,非子树节点的深度+1。考虑删除,子树节点深度-1.用线段树维护即可。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LD double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mm=1e5+9,inf=1e9;
int m,top,op[mm],key[mm],a[mm],tag[mm*4],size[mm*4],dep2[mm],fa[mm],son[mm][2];
int retag(int now){
    tag[now*2]+=tag[now];
    tag[now*2+1]+=tag[now];
    tag[now]=0;
}
int vis(int now,int l,int r,int v){
    int mid=(l+r)>>1;
    if(l==r){
        dep2[l]=tag[now];
        return (size[now])?l:0;
    }
    retag(now);
    if(mid<v)return vis(now*2+1,mid+1,r,v);
    else return vis(now*2,l,mid,v);
}
void add(int now,int l,int r,int u,int v){
    int mid=(l+r)>>1;
    if(l==r){
        tag[now]=(v>0)?v:0;
        size[now]=v>0;
        return;
    }
    retag(now);
    if(u<=mid)add(now*2,l,mid,u,v);
    else add(now*2+1,mid+1,r,u,v);
    size[now]=size[now*2]+size[now*2+1];
}
void up(int now,int l,int r,int u,int v,int w){
    int mid=(l+r)>>1;
    if((l==u)&&(r==v)){
        tag[now]+=w;
        return;
    }
    retag(now);
    if(v<=mid)up(now*2,l,mid,u,v,w);
    else if(mid<u)up(now*2+1,mid+1,r,u,v,w);
    else{
        up(now*2,l,mid,u,mid,w);
        up(now*2+1,mid+1,r,mid+1,v,w);
    }
}
multiset<int> s;
int main(){
    //freopen("splay.in","r",stdin);
    //freopen("splay.out","w",stdout);
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&m);
    fo(i,1,m){
        scanf("%d",&op[i]);
        if(op[i]==1){
            scanf("%d",&key[i]);
            a[++a[0]]=key[i];
        }
    }
    sort(a+1,a+a[0]+1);
    fo(i,1,m)if(key[i])key[i]=lower_bound(a+1,a+a[0]+1,key[i])-a;
    fo(i,1,m){
        if(op[i]==1){
            int tmp,tmp2,tmp3=1;
            if(s.empty())tmp=tmp2=0;
            else{
                multiset<int>::iterator x=s.lower_bound(key[i]);
                if(x!=s.end()){
                    tmp2=*x;
                    if(x==s.begin())tmp=0;
                    else tmp=*(--x);
                }
                else{
                    tmp2=0;
                    if(x==s.begin())tmp=0;
                    else tmp=*(--x);
                }
            }
            if(tmp)vis(1,1,a[0],tmp);if(tmp2)vis(1,1,a[0],tmp2);
            if(tmp&&(!son[tmp][1])){
                fa[key[i]]=tmp;
                son[tmp][1]=key[i];
                tmp3=dep2[tmp]+1;
            }else if(tmp2){
                fa[key[i]]=tmp2;
                son[tmp2][0]=key[i];
                tmp3=dep2[tmp2]+1;
            }else top=key[i];
            add(1,1,a[0],key[i],tmp3);
            printf("%d\n",tmp3);s.insert(key[i]);
        }else if(op[i]==2){
            int tmp=*s.begin(),tmp2=top,tmp3=fa[tmp];
            vis(1,1,a[0],tmp);
            printf("%d\n",dep2[tmp]);
            if(!tmp3)continue;
            up(1,1,a[0],tmp3,a[0],1);
            add(1,1,a[0],tmp,1);
            son[tmp3][0]=son[tmp][1];
            if(son[tmp][1])fa[son[tmp][1]]=tmp3;
            fa[tmp2]=tmp;
            son[tmp][1]=tmp2;
            fa[tmp]=0;top=tmp;
        }else if(op[i]==3){
            int tmp=*(--s.end()),tmp2=top,tmp3=fa[tmp];
            vis(1,1,a[0],tmp);
            printf("%d\n",dep2[tmp]);
            if(!tmp3)continue;
            up(1,1,a[0],1,tmp3,1);
            add(1,1,a[0],tmp,1);
            son[tmp3][1]=son[tmp][0];
            if(son[tmp][0])fa[son[tmp][0]]=tmp3;
            fa[tmp2]=tmp;
            son[tmp][0]=tmp2;
            fa[tmp]=0;top=tmp;
        }else if(op[i]==4){
            int tmp=*s.begin(),tmp2=fa[tmp];
            vis(1,1,a[0],tmp);s.erase(s.begin());
            printf("%d\n",dep2[tmp]);
            if(!tmp2){
                top=son[tmp][1];
                add(1,1,a[0],tmp,-1);
                fa[tmp]=fa[son[tmp][1]]=0;
                if(tmp!=a[0])up(1,1,a[0],tmp+1,a[0],-1);
                continue;
            }
            if(tmp+1<tmp2)up(1,1,a[0],tmp+1,tmp2-1,-1);
            add(1,1,a[0],tmp,-1);
            son[tmp2][0]=son[tmp][1];
            if(son[tmp][1])fa[son[tmp][1]]=tmp2;
        }else{
            int tmp=*(--s.end()),tmp2=fa[tmp];
            vis(1,1,a[0],tmp);s.erase(--s.end());
            printf("%d\n",dep2[tmp]);
            if(!tmp2){
                top=son[tmp][0];
                add(1,1,a[0],tmp,-1);
                fa[tmp]=fa[son[tmp][0]]=0;
                if(tmp!=1)up(1,1,a[0],1,tmp-1,-1);
                continue;
            }
            if(tmp2+1<tmp)up(1,1,a[0],tmp2+1,tmp-1,-1);
            add(1,1,a[0],tmp,-1);
            son[tmp2][1]=son[tmp][0];
            if(son[tmp][0])fa[son[tmp][0]]=tmp2;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值