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的写法有点奇怪……不支持换根的写法)
 #include<map>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
map<int ,int > MAP;
int m,top;
struct DT{
int ch[],CH[],fa,FA;
int size,num;
};
struct Splay{
DT data[];
void splay(int x){
int fa,fafa;
for(fa=data[x].fa;che(x);roll(x),fa=data[x].fa){
fafa=data[fa].fa;
if(che(fa)){
if((data[fa].ch[]==x)^(data[fafa].ch[]==fa))
roll(x);
else
roll(fa);
}
}
}
void roll(int x){
int fa=data[x].fa,fafa=data[fa].fa,wh=data[fa].ch[]==x;
data[fa].ch[wh]=data[x].ch[wh^],data[data[fa].ch[wh]].fa=fa;
data[fa].fa=x,data[x].ch[wh^]=fa;
data[x].fa=fafa;
if(data[fafa].ch[]==fa||data[fafa].ch[]==fa)
data[fafa].ch[data[fafa].ch[]==fa]=x;
up(fa),up(x);
}
bool che(int x){
return data[data[x].fa].ch[]==x||data[data[x].fa].ch[]==x;
}
void up(int x){
data[x].size=data[data[x].ch[]].size+data[data[x].ch[]].size+;
}
};
struct LCT{
Splay T;
int root;
void Mak_data(int id,int num){
T.data[id].ch[]=T.data[id].ch[]=T.data[id].CH[]=T.data[id].CH[]=;
T.data[id].FA=T.data[id].fa=;
T.data[id].size=,T.data[id].num=num;
}
void Link(int fa,int son,int wh){
Access(son),T.splay(son);
T.data[son].FA=fa;
T.data[son].fa=fa;
T.data[fa].CH[wh]=son;
Access(son);
}
void Cut(int fa,int son){
Access(fa),T.splay(son);
T.data[son].FA=T.data[son].fa=;
T.data[fa].CH[T.data[fa].CH[]==son]=;
}
void Access(int x){
int y=;
while(x){
T.splay(x);
T.data[x].ch[]=y,T.up(x);
y=x,x=T.data[x].fa;
}
}
};
LCT lct;
inline void in(int &ans)
{
ans=;bool p=false;char ch=getchar();
while((ch>'' || ch<'')&&ch!='-') ch=getchar();
if(ch=='-') p=true,ch=getchar();
while(ch<=''&&ch>='') ans=ans*+ch-'',ch=getchar();
if(p) ans=-ans;
}
int main()
{
int i,j,k,templ,tempr,temp,f,c;
map<int ,int >::iterator iter;
in(m);
lct.root=;
for(i=;i<=m;i++){
in(j);
if(j==){
in(k);
lct.Mak_data(++top,k);
if(lct.root){
iter=MAP.upper_bound(k);
tempr=iter->second,templ=;
if(iter!=MAP.begin())iter--,templ=iter->second;
if(templ&&!lct.T.data[templ].CH[])
lct.Link(templ,top,);
else
lct.Link(tempr,top,);
}
else
lct.root=top;
lct.Access(top),lct.T.splay(top);
printf("%d\n",lct.T.data[top].size);
MAP[k]=top;
}
else{
iter=MAP.end(),iter--;
if(j&)
temp=iter->second;
else
temp=MAP.begin()->second;
lct.Access(temp),lct.T.splay(temp);
printf("%d\n",lct.T.data[temp].size);
if(lct.T.data[temp].FA&&lct.T.data[temp].CH[!(j&)]){
f=lct.T.data[temp].FA,c=lct.T.data[temp].CH[!(j&)];
lct.Cut(temp,c),lct.Cut(f,temp);
lct.Link(f,c,j&);
}
else{
if(lct.T.data[temp].FA||lct.T.data[temp].CH[!(j&)])
if(lct.T.data[temp].FA)
lct.Cut(lct.T.data[temp].FA,temp);
else
lct.root=lct.T.data[temp].CH[!(j&)],lct.Cut(temp,lct.T.data[temp].CH[!(j&)]);
else{
if(j>)
lct.root=,MAP.erase(lct.T.data[temp].num);
continue;
}
}
if(j<)
lct.Link(temp,lct.root,!(j&)),lct.root=temp;
else
MAP.erase(lct.T.data[temp].num);
}
}
}

bzoj P4825 [Hnoi2017]单旋——solution的更多相关文章

  1. bzoj 4825: [Hnoi2017]单旋 [lct]

    4825: [Hnoi2017]单旋 题意:有趣的spaly hnoi2017刚出来我就去做,当时这题作死用了ett,调了5节课没做出来然后发现好像直接用lct就行了然后弃掉了... md用lct不知 ...

  2. 【刷题】BZOJ 4825 [Hnoi2017]单旋

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...

  3. BZOJ:4825: [Hnoi2017]单旋

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...

  4. bzoj 4825: [Hnoi2017]单旋【dfs序+线段树+hash】

    这个代码已经不是写丑那么简单了--脑子浆糊感觉np++分分钟想暴起打死我--就这还一遍A过了-- 先都读进来hash一下,因为是平衡树所以dfs序直接按照点值来就好 对于每个操作: 1:set维护已插 ...

  5. 【LG3721】[HNOI2017]单旋

    [LG3721][HNOI2017]单旋 题面 洛谷 题解 20pts 直接模拟\(spaly\)的过程即可. 100pts 可以发现单旋最大.最小值到根,手玩是有显然规律的,发现只需要几次\(lin ...

  6. 4825: [Hnoi2017]单旋

    4825: [Hnoi2017]单旋 链接 分析: 以后采取更保险的方式写代码!!!81行本来以为不特判也可以,然后就总是比答案大1,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...

  7. [BZOJ4825][HNOI2017]单旋(线段树+Splay)

    4825: [Hnoi2017]单旋 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 667  Solved: 342[Submit][Status][ ...

  8. 【BZOJ4825】[Hnoi2017]单旋 线段树+set

    [BZOJ4825][Hnoi2017]单旋 Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能 ...

  9. bzoj4825 [Hnoi2017]单旋

    Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...

随机推荐

  1. 记录php漏洞--宇宙最强语言 PHP 爆出 DoS 漏洞,可以直接灌满 CPU

    站长之家(Chinaz.com)5月20日消息  近日,PHP被爆出存在远程DOS漏洞,若黑客利用该漏洞构造PoC发起连接,容易导致目标主机CPU被迅速消耗.此漏洞涉及众多PHP版本,因而影响范围极大 ...

  2. 聊聊并发(三)Java线程池的分析和使用

    1.    引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行. ...

  3. Android多媒体整体架构图

    Android多媒体整体架构图 MediaPlayer框架图 Camera框架图 SoundRecorder框架图 VideoCamera框架图 OpenCore与Skia ALSA Audio框架图 ...

  4. How React Works (一)首次渲染

    How React Works (一)首次渲染 一.前言 本文将会通过一个简单的例子,结合React源码(v 16.4.2)来说明 React 是如何工作的,并且帮助读者理解 ReactElement ...

  5. python的 del 函数是删对象还是删引用

    1.首先介绍下python的对象引用 1)Python中不存在传值调用,一切传递的都是对象引用,也可以认为是传址调用.即Python不允许程序员选择采用传值或传引用.Python参数传递采用的是“传对 ...

  6. oracle_jdbc_Query

    本例子程序是根据马士兵老师所讲+自己的注释.写的比较全面,特别是最后释放资源的代码. package com.ayang.jdbc; import java.sql.*; public class T ...

  7. Android硬件抽象层(HAL)深入剖析(三)【转】

    前面分析了android HAL层是如何搜索硬件模块的动态共享库的,其实就是在"system/lib/hw/"或者"/vendor/lib/hw/"这两个路径下 ...

  8. notepad++上搭建gtk+2.0/3.x开发环境

    前言 老师布置了一道题需要用到图形界面,于是开始找图形库.最后选择了gtk+图形库,然后折腾了大概一天. 这里记录自己新学到的知识,同时也给后来者一些便利. 准备 下载以下内容 notepad++(由 ...

  9. android学习-Toast的延迟时间

    一般显示Toast都是一条语句够了 Toast.makeText(Context context, CharSequence text, int duration) Context是要显示Toast的 ...

  10. Python 日期时间处理模块学习笔记

    来自:标点符的<Python 日期时间处理模块学习笔记> Python的时间处理模块在日常的使用中用的不是非常的多,但是使用的时候基本上都是要查资料,还是有些麻烦的,梳理下,便于以后方便的 ...