zoukankan      html  css  js  c++  java
  • [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 } 
  • 相关阅读:
    BFS visit tree
    Kth Largest Element in an Array 解答
    Merge k Sorted Lists 解答
    Median of Two Sorted Arrays 解答
    Maximal Square 解答
    Best Time to Buy and Sell Stock III 解答
    Best Time to Buy and Sell Stock II 解答
    Best Time to Buy and Sell Stock 解答
    Triangle 解答
    Unique Binary Search Trees II 解答
  • 原文地址:https://www.cnblogs.com/Y-E-T-I/p/7625334.html
Copyright © 2011-2022 走看看