• [AH/HNOI2017]单旋


    题目描述

    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号操作。

    对于不是H国的人,你可能需要了解一些spaly的知识,才能完成国王的任务:

    1. spaly是一棵二叉树,满足对于任意一个节点x,它如果有左孩子lx,那么lx的关键码小于x的关键码。如果有右孩子rx,那么rx的关键码大于x的关键码。

    2. 一个节点在spaly的深度定义为:从根节点到该节点的路径上一共有多少个节点(包括自己)。

    3. 单旋操作是对于一棵树上的节点x来说的。一开始,设f为x在树上的父亲。如果x为f的左孩子,那么执行zig(x)操作(如上图中,左边的树经过zig(x)变为了右边的树),否则执行zag(x)操作(在上图中,将右边的树经过zag(f)就变成了左边的树)。每当执行一次zig(x)或者zag(x),x的深度减小1,如此反复,直到x为根。总之,单旋x就是通过反复执行zig和zag将x变为根。

    输入输出格式

    输入格式:

    输入文件名为 splay.in。

    第一行单独一个正整数 m (1 <= m <= 10^5)。

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

    输出格式:

    输出文件名为 splay.out。

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

    输入输出样例

    输入样例#1:
    5
    1 2
    1 1
    1 3
    4 
    5
    输出样例#1:
    1 
    2 
    2
    2 
    2

    说明

    20%的数据满足: 1 <= m <= 1000。

    另外 30%的数据满足: 不存在 4,5 操作。

    100%的数据满足: 1<=m<=10^5; 1<=key<=10^9。 所有出现的关键码互不相同。 任何一个非插入操作,一定保证树非空。 在未执行任何操作之前,树为空。

    这道题的关键之处在于:除了插入以外,只操作最大/最小值

    所以每次旋转只有一个固定方向,对树的影响其实不大。

    以旋转最小值为例,除了最小值的右子树,其他节点深度+1,如果删去的话

    因为它作为根节点,没有左子树,所以直接删去,所有点深度-1。如果不删,最小点深度为1

    对与插入,节点的深度是当前spaly中比它小中最大的、比它大的中最小的,两个节点深度更大值+1

    用线段树维护深度,线段树中,每个叶节点表示第i大的值得深度信息,显然这需要离线

    cnt[rt]表示区间内有多少个点加入了树

    mark[rt]区间加的延迟标记

    mi[rt]当前区间最小的加入了树的节点的深度

    mx[rt]当前区间最大的加入了树的节点的深度

    t[rt]表示该区间存在的最小深度,未加入树的空节点不算

    这样我们维护这个信息

    对于mi,只要存在左节点就赋值,否则就赋值右节点,mx同理

    查找小于插入值的最大节点的话,当线段树的右端点为插入节点的大小时

    mx[rt]显然就是小于插入值的最大节点的深度(大于的最小值同理,用mi就行了)

    对于找到最小值(深度为d)的右子树,用一个find函数,找到q为最小值的父亲

    这样找到的右子树肯定是一个连续区间,所以(其他节点深度+1)=>([q,n]+1)

    如何找到,如果左节点的最小深度大于d或为空,显然找右节点,否则找左节点

    这样可以找到深度刚好小于最小值且大于最小值的节点q

    似乎和正常的splay一样,还要加入最大值和最小值,也就是线段树中0和n+1

    这题代码略长,有3.6KB,调了一天

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 using namespace std;
      7 struct Ask
      8 {
      9     int x,id;
     10 }A[1000001];
     11 const int N=100005,M=400005;
     12 int m,n,a[N],opt[N],now,mi[M],mx[M],cnt[M],t[M],mark[M],dep;
     13 bool cmp(Ask a,Ask b)
     14 {
     15     return a.x<b.x;
     16 }
     17 int gi()
     18 {
     19     char ch=getchar();
     20     int x=0;
     21     while (ch<'0'||ch>'9') ch=getchar();
     22     while (ch>='0'&&ch<='9') 
     23     {
     24         x=x*10+ch-'0';
     25         ch=getchar();
     26     }
     27     return x;
     28 }
     29 void pushdown(int rt)
     30 {
     31     if (mark[rt])
     32     {
     33         if (cnt[rt*2])
     34         {
     35             t[rt*2]+=mark[rt];
     36             mi[rt*2]+=mark[rt];
     37             mx[rt*2]+=mark[rt];
     38             mark[rt*2]+=mark[rt];
     39         }
     40         if (cnt[rt*2+1])
     41         {
     42             t[rt*2+1]+=mark[rt];
     43             mi[rt*2+1]+=mark[rt];
     44             mx[rt*2+1]+=mark[rt];
     45             mark[rt*2+1]+=mark[rt];
     46         }
     47         mark[rt]=0;
     48     }
     49 }
     50 void pushup(int rt)
     51 {
     52     if (cnt[rt*2+1]) mx[rt]=mx[rt*2+1];
     53     else mx[rt]=mx[rt*2];
     54     if (cnt[rt*2]) mi[rt]=mi[rt*2];
     55     else mi[rt]=mi[rt*2+1];
     56     t[rt]=min(t[rt*2],t[rt*2+1]);
     57     cnt[rt]=cnt[rt*2]+cnt[rt*2+1]; 
     58 }
     59 int get1(int rt,int l,int r,int v)
     60 {
     61     if (v==r)
     62     {
     63         return mx[rt];
     64     }
     65     pushdown(rt); 
     66     int mid=(l+r)/2;
     67     if (v<=mid) return get1(rt*2,l,mid,v);
     68     int x=get1(rt*2+1,mid+1,r,v);
     69     if (x>0) return x;
     70     return mx[rt*2];
     71 }
     72 int get2(int rt,int l,int r,int v)
     73 {
     74     if (v==l)
     75     {
     76         return mi[rt];
     77     }
     78     pushdown(rt); 
     79     int mid=(l+r)/2;
     80     if (v>mid) return get2(rt*2+1,mid+1,r,v);
     81     int x=get2(rt*2,l,mid,v);
     82     if (x>0) return x;
     83     return mi[rt*2+1];
     84 }
     85 void insert(int rt,int l,int r,int d,int v)
     86 {
     87     if (l==r)
     88     {
     89         t[rt]=mi[rt]=mx[rt]=d;
     90         cnt[rt]=1;
     91         return;
     92     }
     93     pushdown(rt); 
     94     int mid=(l+r)/2;
     95     if (v<=mid) insert(rt*2,l,mid,d,v);
     96     else insert(rt*2+1,mid+1,r,d,v);
     97     pushup(rt);
     98 }
     99 int getmin(int rt,int l,int r)
    100 {
    101     if (l==r)
    102     {
    103         dep=t[rt];
    104         return l;
    105     }
    106     pushdown(rt);
    107     int mid=(l+r)/2;
    108     if (cnt[rt*2]>0) return getmin(rt*2,l,mid);
    109     else return getmin(rt*2+1,mid+1,r);
    110 }
    111 int getmax(int rt,int l,int r)
    112 {
    113     if (l==r)
    114     {
    115         dep=t[rt];
    116         return l;
    117     }
    118     pushdown(rt);
    119     int mid=(l+r)/2;
    120     if (cnt[rt*2+1]>0) return getmax(rt*2+1,mid+1,r);
    121     else return getmax(rt*2,l,mid);
    122 }
    123 int find1(int rt,int l,int r,int d)
    124 {
    125     if (l==r)
    126         return l;
    127     pushdown(rt);
    128     int mid=(l+r)/2;
    129     if (cnt[rt*2]==0||t[rt*2]>d) return find1(rt*2+1,mid+1,r,d);
    130     else return find1(rt*2,l,mid,d); 
    131 }
    132 int find2(int rt,int l,int r,int d)
    133 {
    134     if (l==r)
    135         return l;
    136     pushdown(rt);
    137     int mid=(l+r)/2;
    138     if (cnt[rt*2+1]==0||t[rt*2+1]>d) return find2(rt*2,l,mid,d);
    139     else return find2(rt*2+1,mid+1,r,d); 
    140 }
    141 void change(int rt,int l,int r,int L,int R,int x)
    142 {
    143     if (!cnt[rt]) return;
    144     if (l>=L&&r<=R)
    145     {
    146         mark[rt]+=x;
    147         t[rt]+=x;
    148         mx[rt]+=x;mi[rt]+=x;
    149         return;
    150     }
    151     pushdown(rt);
    152     int mid=(l+r)/2;
    153     if (L<=mid) change(rt*2,l,mid,L,R,x);
    154     if (R>mid) change(rt*2+1,mid+1,r,L,R,x);
    155 pushup(rt);
    156 }
    157 void Delet(int rt,int l,int r,int v)
    158 {
    159     if (l==r)
    160     {
    161         t[rt]=n;
    162         mx[rt]=mi[rt]=cnt[rt]=0;
    163         return;
    164     }
    165     pushdown(rt);
    166     int mid=(l+r)/2;
    167     if (v<=mid) Delet(rt*2,l,mid,v);
    168     else Delet(rt*2+1,mid+1,r,v);
    169     pushup(rt);
    170 }
    171 int main()
    172 {int i;
    173  cin>>m;
    174  for (i=1;i<=m;i++)
    175  {
    176      opt[i]=gi();
    177      if (opt[i]==1)
    178      {
    179          A[++n].x=gi();
    180          A[n].id=n;
    181      }
    182  }
    183  sort(A+1,A+n+1,cmp);
    184  for (i=1;i<=n;i++) a[A[i].id]=i;
    185  n++;
    186  now=0;
    187  memset(t,127,sizeof(t));
    188  for (i=1;i<=m;i++)
    189  {
    190      if (opt[i]==1)
    191      {
    192          now++;
    193          insert(1,0,n,dep=max(get1(1,0,n,a[now]),get2(1,0,n,a[now]))+1,a[now]);
    194      }
    195      else if (opt[i]==2||opt[i]==4)
    196      {
    197          int p=getmin(1,0,n),q;
    198          Delet(1,0,n,p);
    199          q=find1(1,0,n,dep);
    200          change(1,0,n,q,n,1);
    201          if (opt[i]==4)
    202          change(1,0,n,0,n,-1);
    203          else insert(1,0,n,1,p);
    204      }
    205      else if (opt[i]==3||opt[i]==5)
    206      {
    207          int p=getmax(1,0,n),q;
    208          Delet(1,0,n,p);
    209          q=find2(1,0,n,dep);
    210         change(1,0,n,0,q,1);
    211         if (opt[i]==5)
    212             change(1,0,n,0,n,-1);
    213         else insert(1,0,n,1,p);
    214      }
    215      printf("%d
    ",dep); 
    216  }
    217 } 
  • 相关阅读:
    微信公众平台二次开发需要配置的几个地址与参数
    Extjs4.1+desktop+SSH2 定义程序入口
    Extjs4.1+desktop+SSH2 搭建环境 项目能跑起来
    Liunx开发(Extjs4.1+desktop+SSH2超强视频教程实践)(2)
    Liunx开发(Extjs4.1+desktop+SSH2超强视频教程实践)(1)
    增量补丁打包器(我也不是想这么干的)
    部署git服务器(Windows Server 2008)
    测试发布(maven-assembly-plugin看好你哦)
    工作流性能优化(敢问activiti有扩展性?)(3)
    工作流性能优化(敢问activiti有扩展性?)(2)
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7625334.html
Copyright © 2020-2023  润新知