zoukankan      html  css  js  c++  java
  • [BZOJ4825][HNOI2017]单旋(线段树+Splay)

    4825: [Hnoi2017]单旋

    Time Limit: 10 Sec  Memory Limit: 256 MB
    Submit: 667  Solved: 342
    [Submit][Status][Discuss]

    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

    HINT

     

    Source

    代码用时:2h

    感觉代码难度很低啊为什么我还是花了很长时间。。

    思维比较简单,只要把三种操作想清楚了就差不多了。

    比较自然的想法应该是Splay,但线段树实现复杂度和常数都更优。

    线段树做法网上好像只找到一个(而且没有标记永久化的代码并不是很优美常数也稍大)

    这里有一个自认为比较优的做法。

    (本题线段树做法的思想还是有点巧妙的,利用了题目只单旋最值的设定)

    12140KB,1108ms 

     1 #include<set>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<iostream>
     5 #define rep(i,l,r) for (int i=l; i<=r; i++)
     6 using namespace std;
     7 
     8 template<typename T>inline void rd(T &x){
     9     int t; char ch;
    10     for (t=0; !isdigit(ch=getchar()); t=(ch=='-'));
    11     for (x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0');
    12     if (t) x=-x;
    13 }
    14 
    15 const int N=200100;
    16 int m,tot,d,rt,D[N<<2],rs[N],ls[N],fa[N],a[N],b[N],op[N][2];
    17 set<int>S;
    18 set<int>::iterator it,sub,pro;
    19 
    20 void mdf(int x,int L,int R,int l,int r,int k){
    21     if (L==l && R==r){ D[x]+=k; return; }
    22     int mid=(L+R)>>1;
    23     if (r<=mid) mdf(x<<1,L,mid,l,r,k);
    24     else if (l>mid) mdf((x<<1)|1,mid+1,R,l,r,k);
    25         else mdf(x<<1,L,mid,l,mid,k),mdf((x<<1)|1,mid+1,R,mid+1,r,k);
    26 }
    27 
    28 int que(int x,int L,int R,int k){
    29     if (L==R) return D[x];
    30     int mid=(L+R)>>1;
    31     if (k<=mid) return D[x]+que(x<<1,L,mid,k);
    32         else return D[x]+que((x<<1)|1,mid+1,R,k);
    33 }
    34 
    35 int main(){
    36     freopen("bzoj4825.in","r",stdin);
    37     freopen("bzoj4825.out","w",stdout);
    38     rd(m);
    39     rep(i,1,m){
    40         rd(op[i][0]); if (op[i][0]==1) rd(op[i][1]);
    41         if (op[i][0]==1) b[++tot]=op[i][1];
    42     }
    43     sort(b+1,b+tot+1); tot=unique(b+1,b+tot+1)-b-1;
    44    rep(i,1,m) if (op[i][0]==1) a[i]=lower_bound(b+1,b+tot+1,op[i][1])-b;
    45     rep(i,1,m){
    46         if (op[i][0]==1){
    47             if (S.empty()) rt=a[i],d=1;
    48             else{
    49                 it=S.upper_bound(a[i]),sub=it;
    50                 if (sub==S.begin()) ls[*sub]=a[i],fa[a[i]]=*sub,d=que(1,1,tot,*sub)+1;
    51                 else{
    52                     pro=--it;
    53                     if (sub==S.end()) rs[*pro]=a[i],fa[a[i]]=*pro,d=que(1,1,tot,*pro)+1;
    54                     else if (que(1,1,tot,*sub)<que(1,1,tot,*pro))
    55                      rs[*pro]=a[i],fa[a[i]]=*pro,d=que(1,1,tot,*pro)+1;
    56                         else ls[*sub]=a[i],fa[a[i]]=*sub,d=que(1,1,tot,*sub)+1;
    57                 }
    58             }
    59             printf("%d
    ",d); mdf(1,1,tot,a[i],a[i],d-que(1,1,tot,a[i]));
    60          S.insert(a[i]);
    61         }
    62         if (op[i][0]==2 || op[i][0]==4){
    63             it=S.begin(); d=que(1,1,tot,*it); printf("%d
    ",d);
    64             if ((*it)!=rt){
    65                 mdf(1,1,tot,1,tot,1); mdf(1,1,tot,*it,*it,-d);
    66                 int t=fa[*it]; ls[t]=fa[*it]=0;
    67                 if (rs[*it]){
    68                     mdf(1,1,tot,(*it)+1,t-1,-1);
    69                fa[rs[*it]]=t; ls[t]=rs[*it];
    70                 }
    71             rs[*it]=rt; fa[rt]=*it; rt=*it;
    72             }
    73             if (op[i][0]==4) rt=rs[*it],rs[*it]=fa[rt]=0,mdf(1,1,tot,1,tot,-1),S.erase(*it);
    74         }
    75         if (op[i][0]==3 || op[i][0]==5){
    76             it=S.end(); it--; d=que(1,1,tot,*it); printf("%d
    ",d);
    77             if ((*it)!=rt){
    78                 mdf(1,1,tot,1,tot,1); mdf(1,1,tot,*it,*it,-d);
    79                 int t=fa[*it]; rs[t]=fa[*it]=0;
    80                 if (ls[*it]){
    81                     mdf(1,1,tot,t+1,(*it)-1,-1);
    82                fa[ls[*it]]=t; rs[t]=ls[*it];
    83                 }
    84             ls[*it]=rt; fa[rt]=*it; rt=*it;
    85             }
    86             if (op[i][0]==5) rt=ls[*it],ls[*it]=fa[rt]=0,mdf(1,1,tot,1,tot,-1),S.erase(*it);
    87         }
    88     }
    89     return 0;
    90 }

     惊了,splay跑的比线段树还快。

    5752KB,900ms

    代码用时:1h

    一开始想自己写出来,思路并不复杂但代码很繁琐(模板占了大部分但想敲对并不容易)。

    看了网上的代码抄了一份下来竟然1A,可能是我抄代码的经历中最顺利的一次吧。

    但抄来的代码终究不是自己的,要勇于写这种代码稍长的题目,后面还会有比这长的多的题!

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<algorithm>
      4 #define ls c[x][0]
      5 #define rs c[x][1]
      6 #define rep(i,l,r) for (int i=l; i<=r; i++)
      7 using namespace std;
      8 
      9 template<typename T>inline void rd(T &x){
     10    int t; char ch;
     11    for (t=0; !isdigit(ch=getchar()); t=(ch=='-'));
     12    for (x=ch-'0'; isdigit(ch=getchar()); x=x*10+ch-'0');
     13    if (t) x=-x;
     14 }
     15 
     16 const int inf=2000000000,N=100100;
     17 int n,cnt,rt,Q,fa[N],v[N],sz[N],c[N][2],s[N],dep[N],mn[N];
     18 
     19 void push(int x){
     20    v[ls]+=v[x]; dep[ls]+=v[x]; mn[ls]+=v[x];
     21    v[rs]+=v[x]; dep[rs]+=v[x]; mn[rs]+=v[x];
     22    v[x]=0;
     23 }
     24 
     25 void upd(int x){
     26    sz[x]=sz[ls]+sz[rs]+1; mn[x]=dep[x];
     27    if (ls) mn[x]=min(mn[x],mn[ls]);
     28    if (rs) mn[x]=min(mn[x],mn[rs]);
     29 }
     30 
     31 void rot(int &rt,int x){
     32    int y=fa[x],z=fa[y],w=(c[y][1]==x);
     33    if (y==rt) rt=x; else c[z][c[z][1]==y]=x;
     34    fa[x]=z; fa[y]=x; fa[c[x][w^1]]=y;
     35    c[y][w]=c[x][w^1]; c[x][w^1]=y; upd(y);
     36 }
     37 
     38 void splay(int &rt,int x){
     39    while (x!=rt) {
     40       int y=fa[x],z=fa[y];
     41       if (y!=rt){
     42          if ((c[z][1]==y)^(c[y][1]==x)) rot(rt,x); else rot(rt,y);
     43       }
     44       rot(rt,x);
     45    }
     46    upd(x);
     47 }
     48 
     49 void ins(int &x,int S,int d,int lst){
     50    if (!x){
     51       x=++cnt, s[cnt]=S; dep[cnt]=mn[cnt]=d;
     52       sz[cnt]=1; fa[cnt]=lst; return;
     53    }
     54    ins(c[x][S>s[x]],S,d,x); upd(x);
     55 }
     56 
     57 int getpre(int x,int S){
     58    if (!x) return 0;
     59    if (v[x]) push(x);
     60    if (s[x]>S) return getpre(c[x][0],S);
     61    Q=getpre(c[x][1],S);
     62    if (Q) return Q; else return x;
     63 }
     64 
     65 int getnxt(int x,int S){
     66    if (!x) return 0;
     67    if (v[x]) push(x);
     68    if (s[x]<S) return getnxt(rs,S);
     69    Q=getnxt(ls,S);
     70    if (Q) return Q; else return x;
     71 }
     72 
     73 int find(int x,int k){
     74    if (v[x]) push(x);
     75    if (sz[ls]+1==k) return x;
     76    if (sz[ls]+1<k) return find(rs,k-sz[ls]-1);
     77    return find(ls,k);
     78 }
     79 
     80 int getl(int x,int d){
     81    if (!x) return 0;
     82    if (v[x]) push(x);
     83    if (min(mn[ls],dep[x])>=d) return getl(rs,d)+sz[ls]+1;
     84       else return getl(ls,d);
     85 }
     86 
     87 int getr(int x,int d){
     88    if (!x) return 0;
     89    if (v[x]) push(x);
     90    if (min(mn[rs],dep[x])>=d) return getr(ls,d)+sz[rs]+1;
     91       else return getr(rs,d);
     92 }
     93 
     94 int split(int l,int r){
     95    int t1=find(rt,l-1),t2=find(rt,r+1);
     96    splay(rt,t1); splay(c[rt][1],t2); return c[c[rt][1]][0];
     97 }
     98 
     99 void mdf(int l,int r,int ad){
    100    int y=split(l,r); v[y]+=ad; mn[y]+=ad; dep[y]+=ad;
    101 }
    102 
    103 void change(int x,int S){
    104    if (v[x]) push(x);
    105    if (s[x]==S) dep[x]=1; else change(c[x][S>s[x]],S);
    106    upd(x);
    107 }
    108 
    109 int main(){
    110    freopen("bzoj4825.in","r",stdin);
    111    freopen("bzoj4825.out","w",stdout);
    112    rd(n); ins(rt,-inf,inf,0); ins(rt,inf,inf,0); mn[0]=inf;
    113    rep(i,1,n){
    114       int op,x; rd(op);
    115       if (op==1){
    116          rd(x); int t1=getpre(rt,x),t2=getnxt(rt,x);
    117          int D=max(t1>2 ? dep[t1] : 0,t2>2 ? dep[t2] : 0)+1;
    118          ins(rt,x,D,0); splay(rt,cnt); printf("%d
    ",D);
    119       }
    120       if (!(op&1)){
    121          int x=find(rt,2),y=min(getl(rt,dep[x]),sz[rt]-1)-1;
    122          printf("%d
    ",dep[x]); mdf(2,sz[rt]-1,1);
    123          if (y>1) mdf(2,y+1,-1);
    124          change(rt,s[x]);
    125       }
    126       if ((op&1)&&(op>1)){
    127          int x=find(rt,sz[rt]-1),y=min(getr(rt,dep[x]),sz[rt]-1)-1;
    128          printf("%d
    ",dep[x]); mdf(2,sz[rt]-1,1);
    129          if (y>1) mdf(sz[rt]-y,sz[rt]-1,-1);
    130          change(rt,s[x]);
    131       }
    132       if (op>=4){
    133          if (op==4) splay(rt,find(rt,2));
    134                else splay(rt,find(rt,sz[rt]-1));
    135          int l=(op==5),r=l^1,y=c[rt][l];
    136          c[y][r]=c[rt][r]; fa[y]=0; fa[c[rt][r]]=y; rt=y;
    137          v[rt]-=1; upd(rt);
    138       }
    139    }
    140    return 0;
    141 }
  • 相关阅读:
    windows下编译Boost库
    linux下编译Boost库
    CEPH安装教程(下)
    CEPH安装教程(中)
    CEPH安装教程(上)
    nfs使用教程
    iscsi使用教程(下)
    POJ-2096 Collecting Bugs 概率dp
    HDU-3586 Information Disturbing 树形dp+二分
    HDU-1024 Max Sum Plus Plus 动态规划 滚动数组和转移优化
  • 原文地址:https://www.cnblogs.com/HocRiser/p/8299273.html
Copyright © 2011-2022 走看看