zoukankan      html  css  js  c++  java
  • bzoj4825 [Hnoi2017]单旋

    Description

    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 变为根。

    Input

    第一行单独一个正整数 m。
    接下来 m 行,每行描述一个操作:首先是一个操作编号 c∈[1,5],即问题描述中给出的五种操作中的编号,若 c = 1,则再输入一个非负整数 key,表示新插入节点的关键码。
    1≤m≤10^5,1≤key≤10^9
    所有出现的关键码互不相同。任何一个非插入操作,一定保证树非空。在未执行任何操作之前,树为空

    Output

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

    Sample Input

    5
    1 2
    1 1
    1 3
    4
    5

    Sample Output

    1
    2
    2
    2
    2

    正解:$link-cut tree+set$

    这题我能说什么。。考场上没想到,然而考后就想出来了。。

    其实这题是一道大水题吧。。其实连$LCT$都不要用,直接线段树维护就行了。。

    我们可以观察得到:插入结点时一定是插到它的前驱或后继中深度更深的那个下面。

    单旋最值其实就是把最值抽出来,与原来的根相连,然后再把它的子树与它原来的父亲相连。删除操作类似,还简单一些。。

    然后我们直接用$LCT$维护$spaly$。插入,单旋,删除的时候各种$link$,$cut$就行了,顺便再开两个数组维护一下原树的父子关系。

    同时我们要查询前驱,后继,最小值,最大值,这个用$set$就能很方便地解决。

    每次查询深度,就是$makeroot$一下树根,然后$access$并$splay$当前点,直接查询$splay$中子树大小就行了。

    注意细节:如果单旋的时候这个点已经是根就不要$link$,$cut$了。这个地方坑了我好久。。

      1 //It is made by wfj_2048~
      2 #include <algorithm>
      3 #include <iostream>
      4 #include <complex>
      5 #include <cstring>
      6 #include <cstdlib>
      7 #include <cstdio>
      8 #include <vector>
      9 #include <cmath>
     10 #include <queue>
     11 #include <stack>
     12 #include <map>
     13 #include <set>
     14 #define inf (1<<30)
     15 #define N (100010)
     16 #define ls (x<<1)
     17 #define rs (x<<1|1)
     18 #define il inline
     19 #define RG register
     20 #define ll long long
     21 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
     22 
     23 using namespace std;
     24 
     25 set <int> tr;
     26 set <int>::iterator r;
     27 
     28 struct data{ int c,k; }q[N];
     29 
     30 int hsh[N],rev[N],sz[N],st[N],fa[N],ch[N][2],f[N],c[N][2],m,rt,cnt,tot;
     31 
     32 il int gi(){
     33     RG int x=0,q=1; RG char ch=getchar();
     34     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
     35     if (ch=='-') q=-1,ch=getchar();
     36     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
     37     return q*x;
     38 }
     39 
     40 il int isroot(RG int x){
     41     return ch[fa[x]][0]!=x && ch[fa[x]][1]!=x;
     42 }
     43 
     44 il void pushdown(RG int x){
     45     rev[x]=0,rev[ch[x][0]]^=1,rev[ch[x][1]]^=1;
     46     swap(ch[x][0],ch[x][1]); return;
     47 }
     48 
     49 il void pushup(RG int x){
     50     sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; return;
     51 }
     52 
     53 il void rotate(RG int x){
     54     RG int y=fa[x],z=fa[y],k=ch[y][0]==x;
     55     if (!isroot(y)) ch[z][ch[z][1]==y]=x;
     56     fa[x]=z,ch[y][k^1]=ch[x][k],fa[ch[x][k]]=y;
     57     ch[x][k]=y,fa[y]=x,pushup(y),pushup(x); return;
     58 }
     59 
     60 il void splay(RG int x){
     61     RG int top=0; st[++top]=x;
     62     for (RG int i=x;!isroot(i);i=fa[i]) st[++top]=fa[i];
     63     for (RG int i=top;i;--i) if (rev[st[i]]) pushdown(st[i]);
     64     while (!isroot(x)){
     65     RG int y=fa[x],z=fa[y];
     66     if (!isroot(y)){
     67         if ((ch[z][0]==y)^(ch[y][0]==x)) rotate(x);
     68         else rotate(y);
     69     }
     70     rotate(x);
     71     }
     72     return;
     73 }
     74 
     75 il void access(RG int x){
     76     RG int t=0;
     77     while (x){
     78     splay(x),ch[x][1]=t;
     79     pushup(x),t=x,x=fa[x];
     80     }
     81     return;
     82 }
     83 
     84 il void makeroot(RG int x){
     85     access(x),splay(x),rev[x]^=1; return;
     86 }
     87 
     88 il void link(RG int x,RG int y){
     89     if (!x || !y || x==y) return;
     90     makeroot(x),fa[x]=y; return;
     91 }
     92 
     93 il void cut(RG int x,RG int y){
     94     if (!x || !y || x==y) return;
     95     makeroot(x),access(y),splay(y);
     96     fa[x]=ch[y][0]=0,pushup(y); return;
     97 }
     98 
     99 il int query(RG int x){
    100     makeroot(rt),access(x),splay(x);
    101     return sz[x];
    102 }
    103 
    104 il void update1(RG int x,RG int k){
    105     RG int y=f[x],z=c[x][k];
    106     c[x][k]=rt,f[rt]=x,f[x]=0;
    107     c[y][k^1]=z,f[z]=y; return;
    108 }
    109 
    110 il void update2(RG int x,RG int k){
    111     RG int y=f[x],z=c[x][k]; if (x==rt) rt=z;
    112     c[y][k^1]=z,f[z]=y,c[x][k]=f[x]=0; return;
    113 }
    114 
    115 il void work(){
    116     m=gi(),tr.insert(-inf),tr.insert(inf);
    117     for (RG int i=1;i<=m;++i){
    118     q[i].c=gi(),sz[i]=1;
    119     if (q[i].c==1) q[i].k=gi(),hsh[++tot]=q[i].k;
    120     }
    121     sort(hsh+1,hsh+tot+1),tot=unique(hsh+1,hsh+tot+1)-hsh-1;
    122     for (RG int i=1;i<=m;++i){
    123     if (q[i].c==1){
    124         q[i].k=lower_bound(hsh+1,hsh+tot+1,q[i].k)-hsh;
    125         if (!cnt) tr.insert(rt=q[i].k),++cnt,puts("1"); else{
    126         r=tr.upper_bound(q[i].k); RG int nxt=*r,x=0,d=0,k; --r; RG int pre=*r;
    127         if (pre!=-inf){ k=query(pre); if (d<k) x=pre,d=k; }
    128         if (nxt!=inf){ k=query(nxt); if (d<k) x=nxt,d=k; }
    129         f[q[i].k]=x,c[x][x<q[i].k]=q[i].k,link(x,q[i].k);
    130         ++cnt,tr.insert(q[i].k),printf("%d
    ",d+1);
    131         }
    132     }
    133     if (q[i].c==2){
    134         if (cnt==1) puts("1"); else{
    135         r=tr.begin(),++r; RG int x=*r,y=f[x],z=c[x][1],k=query(x);
    136         if (x!=rt) cut(x,y),cut(x,z),link(x,rt),link(y,z),update1(x,1),rt=x;
    137         printf("%d
    ",k);
    138         }
    139     }
    140     if (q[i].c==3){
    141         if (cnt==1) puts("1"); else{
    142         r=tr.end(),--r,--r; RG int x=*r,y=f[x],z=c[x][0],k=query(x);
    143         if (x!=rt) cut(x,y),cut(x,z),link(x,rt),link(y,z),update1(x,0),rt=x;
    144         printf("%d
    ",k);
    145         }
    146     }
    147     if (q[i].c==4){
    148         if (cnt==1) puts("1"),tr.erase(tr.find(rt)),rt=0,--cnt; else{
    149         r=tr.begin(),++r; RG int x=*r,y=f[x],z=c[x][1],k=query(x);
    150         tr.erase(tr.find(x)),--cnt,cut(x,y),cut(x,z),link(y,z);
    151         update2(x,1),printf("%d
    ",k);
    152         }
    153     }
    154     if (q[i].c==5){
    155         if (cnt==1) puts("1"),tr.erase(tr.find(rt)),rt=0,--cnt; else{
    156         r=tr.end(),--r,--r; RG int x=*r,y=f[x],z=c[x][0],k=query(x);
    157         tr.erase(tr.find(x)),--cnt,cut(x,y),cut(x,z),link(y,z);
    158         update2(x,0),printf("%d
    ",k);
    159         }
    160     }
    161     }
    162     return;
    163 }
    164 
    165 int main(){
    166     File("splay");
    167     work();
    168     return 0;
    169 }
  • 相关阅读:
    Chrome快捷键统计
    数据封装
    数据抽象
    linux c++ 服务器端开发面试必看书籍(转载)
    闭包和高阶函数
    this,call,apply,bind
    DOM浏览器window对象模型
    jquery滚动条
    xml教程
    多态
  • 原文地址:https://www.cnblogs.com/wfj2048/p/6767775.html
Copyright © 2011-2022 走看看