• bzoj P4825 [Hnoi2017]单旋——solution


    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
                              ——by bzoj
    http://www.lydsy.com/JudgeOnline/problem.php?id=4825


    这个bzoj的蓝底好赞啊
     
    如果是双旋splay,就是模板题;
    然而是单旋splay,
    直接模拟?
    亲测洛谷t成暴力分(废话,本来就是暴力)
    可能出题人刻意卡了,也可能本来就非常好卡
     
    考虑尝试其他方法;
     
    -操作1 k:      找到k的前驱,后继,如果当前树上前驱无右子节点,则k代表的点挂在前驱右子节点的位置,否则挂在后继的左子节点处
          可以证明前驱的右子节点与后继的左子节点至少有一个不存在;
    (因为在k加入集合之前,集合中不存在大小在k的前驱和后继间的元素,于是若k的前驱有右子树,则k的后继必在此子树上)
    (否则这棵子树上的所有点都在k的前驱后继之间)
    (既然k的后继在k的前驱的右子树上,那么他的左子树上所有元素都在k的前驱后继之间,于是他没有左子节点)
    -操作2和3:   观察单旋最小值或最大值的过程,发现此过程对树的结构影响极少,
          只是把这个代表最大|小值的节点剥离树,
          他的子节点接替他做父节点的子节点(他只有一个子节点),
          然后把剩余整个树挂在他的左|右子树位置,
    -操作4和5:   相当于操作2和3省去最后一个过程,然后保留剩下的树,而把剥离的点弃置
     
    维护树的结构的部分可以直接用LCT;
    查找加入节点的位置——即找前驱后继可以直接用MAP实现
    代码:
    (又一次证明了我的代码能力和STL能力有多烂)
    (LCT的写法有点奇怪……不支持换根的写法)
      1 #include<map>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 using namespace std;
      6 map<int ,int > MAP;
      7 int m,top;
      8 struct DT{
      9     int ch[2],CH[2],fa,FA;
     10     int size,num;
     11 };
     12 struct Splay{
     13     DT data[100010];
     14     void splay(int x){
     15         int fa,fafa;
     16         for(fa=data[x].fa;che(x);roll(x),fa=data[x].fa){
     17             fafa=data[fa].fa;
     18             if(che(fa)){
     19                 if((data[fa].ch[1]==x)^(data[fafa].ch[1]==fa))
     20                     roll(x);
     21                 else
     22                     roll(fa);
     23             }
     24         }
     25     }
     26     void roll(int x){
     27         int fa=data[x].fa,fafa=data[fa].fa,wh=data[fa].ch[1]==x;
     28         data[fa].ch[wh]=data[x].ch[wh^1],data[data[fa].ch[wh]].fa=fa;
     29         data[fa].fa=x,data[x].ch[wh^1]=fa;
     30         data[x].fa=fafa;
     31         if(data[fafa].ch[0]==fa||data[fafa].ch[1]==fa)
     32             data[fafa].ch[data[fafa].ch[1]==fa]=x;
     33         up(fa),up(x);
     34     }
     35     bool che(int x){
     36         return data[data[x].fa].ch[0]==x||data[data[x].fa].ch[1]==x;
     37     }
     38     void up(int x){
     39         data[x].size=data[data[x].ch[0]].size+data[data[x].ch[1]].size+1;
     40     }
     41 };
     42 struct LCT{
     43     Splay T;
     44     int root;
     45     void Mak_data(int id,int num){
     46         T.data[id].ch[0]=T.data[id].ch[1]=T.data[id].CH[0]=T.data[id].CH[1]=0;
     47         T.data[id].FA=T.data[id].fa=0;
     48         T.data[id].size=1,T.data[id].num=num;
     49     }
     50     void Link(int fa,int son,int wh){
     51         Access(son),T.splay(son);
     52         T.data[son].FA=fa;
     53         T.data[son].fa=fa;
     54         T.data[fa].CH[wh]=son;
     55         Access(son);
     56     }
     57     void Cut(int fa,int son){
     58         Access(fa),T.splay(son);
     59         T.data[son].FA=T.data[son].fa=0;
     60         T.data[fa].CH[T.data[fa].CH[1]==son]=0;
     61     }
     62     void Access(int x){
     63         int y=0;
     64         while(x){
     65             T.splay(x);
     66             T.data[x].ch[1]=y,T.up(x);
     67             y=x,x=T.data[x].fa;
     68         }
     69     }
     70 };
     71 LCT lct;
     72 inline void in(int &ans)
     73 {
     74     ans=0;bool p=false;char ch=getchar();
     75     while((ch>'9' || ch<'0')&&ch!='-') ch=getchar();
     76     if(ch=='-') p=true,ch=getchar();
     77     while(ch<='9'&&ch>='0') ans=ans*10+ch-'0',ch=getchar();
     78     if(p) ans=-ans;
     79 }
     80 int main()
     81 {
     82     int i,j,k,templ,tempr,temp,f,c;
     83     map<int ,int >::iterator iter;
     84     in(m);
     85     lct.root=0;
     86     for(i=1;i<=m;i++){
     87         in(j);
     88         if(j==1){
     89             in(k);
     90             lct.Mak_data(++top,k);
     91             if(lct.root){
     92                 iter=MAP.upper_bound(k);
     93                 tempr=iter->second,templ=0;
     94                 if(iter!=MAP.begin())iter--,templ=iter->second;
     95                 if(templ&&!lct.T.data[templ].CH[1])
     96                     lct.Link(templ,top,1);
     97                 else
     98                     lct.Link(tempr,top,0);
     99             }
    100             else
    101                 lct.root=top;
    102             lct.Access(top),lct.T.splay(top);
    103             printf("%d
    ",lct.T.data[top].size);
    104             MAP[k]=top;
    105         }
    106         else{
    107             iter=MAP.end(),iter--;
    108             if(j&1)
    109                 temp=iter->second;
    110             else
    111                 temp=MAP.begin()->second;
    112             lct.Access(temp),lct.T.splay(temp);
    113             printf("%d
    ",lct.T.data[temp].size);
    114             if(lct.T.data[temp].FA&&lct.T.data[temp].CH[!(j&1)]){
    115                 f=lct.T.data[temp].FA,c=lct.T.data[temp].CH[!(j&1)];
    116                 lct.Cut(temp,c),lct.Cut(f,temp);
    117                 lct.Link(f,c,j&1);
    118             }
    119             else{
    120                 if(lct.T.data[temp].FA||lct.T.data[temp].CH[!(j&1)])
    121                     if(lct.T.data[temp].FA)
    122                         lct.Cut(lct.T.data[temp].FA,temp);
    123                     else
    124                         lct.root=lct.T.data[temp].CH[!(j&1)],lct.Cut(temp,lct.T.data[temp].CH[!(j&1)]);
    125                 else{
    126                     if(j>3)
    127                         lct.root=0,MAP.erase(lct.T.data[temp].num);
    128                     continue;
    129                 }
    130             }
    131             if(j<4)
    132                 lct.Link(temp,lct.root,!(j&1)),lct.root=temp;
    133             else
    134                 MAP.erase(lct.T.data[temp].num);
    135         }
    136     }
    137 }
  • 相关阅读:
    MATLAB函数大全 .
    让隐藏的virtualBox菜单重新显示
    【转载】matlab 脚本文件和函数文件
    Ubuntu下用命令行快速打开各类型文件
    关于存货的成本计价方式
    C# .net asp学习笔记
    在DLL中怎么共用一个全局变量
    如果比较表中同类型多列值是否相等?
    统计报表(用ROLLUP 汇总数据)
    UML建模
  • 原文地址:https://www.cnblogs.com/nietzsche-oier/p/8428043.html
Copyright © 2020-2023  润新知