[HNOI 2017]单旋
Description

Input
Output
Sample Input
1 2
1 1
1 3
4
5
Sample Output
1
2
2
2
2
题解
参考了PIPIBoss的做法。
我们手玩一下单旋,发现其如果只改变最大最小值时,主要的性质就是整棵$splay$的形态不会发生很大的改变。
例如旋最小值到根,其实就相当于将最小值的节点取出来,最小值的右儿子连向最小值的父亲。接着再把最小值对应的节点接在原来的根的父亲上。
最大值同理。那么就可以用$LCT$维护了。
对于插入操作,很显然的是这个新插入的节点肯定接向其前驱的右儿子或后继的左儿子。另外,这两个儿子肯定有一个是空的,有一个不空。另外,不空的儿子就是前驱和后继两个中深度较深节点的儿子。
我们将权值离散,找前驱后继就交给$STL-set$,另外还要额外开数组来记录原来$splay$树的形态。
//It is made by Awson on 2017.12.27
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define LD long double
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;
const int N = 1e5;
const int INF = ~0u>>; int m, a[N+], top;
struct Opt {
int opt, x;
}q[N+];
struct Link_Cut_Tree {
int ch[N+][], pre[N+], size[N+], isrt[N+], rev[N+];
set<int>S;
int f[N+], c[N+][], root;
Link_Cut_Tree () {
for (int i = ; i <= N; i++) size[i] = isrt[i] = ;
S.insert(-INF), S.insert(INF);
}
void pushdown(int o) {
if (!o || !rev[o]) return;
int ls = ch[o][], rs = ch[o][];
swap(ch[ls][], ch[ls][]), swap(ch[rs][], ch[rs][]);
rev[ls] ^= , rev[rs] ^= , rev[o] = ;
}
void push(int o) {
if (!isrt[o]) push(pre[o]);
pushdown(o);
}
void pushup(int o) {
if (!o) return;
size[o] = size[ch[o][]]+size[ch[o][]]+;
}
void rotate(int o, int kind) {
int p = pre[o];
ch[p][!kind] = ch[o][kind], pre[ch[o][kind]] = p;
if (isrt[p]) isrt[o] = , isrt[p] = ;
else ch[pre[p]][ch[pre[p]][] == p] = o;
pre[o] = pre[p];
ch[o][kind] = p, pre[p] = o;
pushup(p), pushup(o);
}
void splay(int o) {
push(o);
while (!isrt[o]) {
if (isrt[pre[o]]) rotate(o, ch[pre[o]][] == o);
else {
int p = pre[o], kind = ch[pre[p]][] == p;
if (ch[p][kind] == o) rotate(o, !kind), rotate(o, kind);
else rotate(p, kind), rotate(o, kind);
}
}
}
void access(int o) {
int y = ;
while (o) {
splay(o); size[o] -= size[ch[o][]];
isrt[ch[o][]] = , isrt[ch[o][] = y] = ;
o = pre[y = o];
pushup(o);
}
}
void makeroot(int o) {
access(o), splay(o);
rev[o] ^= , swap(ch[o][], ch[o][]);
}
void link(int x, int y) {
if (!x || !y) return;
makeroot(x); pre[x] = y;
}
void cut(int x, int y) {
if (!x || !y) return;
makeroot(x), access(y), splay(y);
size[y] -= size[x];
ch[y][] = pre[x] = , isrt[x] = ;
}
int query(int x, int y) {
makeroot(x), access(y), splay(y);
return size[ch[y][]]+;
}
int insert(int x) {
if (!root) {
root = x; S.insert(x); return ;
}
int pre = *(--S.lower_bound(x)), nex = *(S.upper_bound(x)), o;
if (pre == -INF) o = nex;
else if (nex == INF) o = pre;
else {
int depx = query(root, pre), depy = query(root, nex);
if (depx > depy) o = pre;
else o = nex;
}
f[x] = o, c[o][x > o] = x; link(o, x); S.insert(x);
return query(root, x);
}
int find_min() {
int o = *(++S.begin()), fa = f[o], child = c[o][];
int ans = query(root, o);
if (o != root) {
cut(o, fa), cut(child, o), link(child, fa), link(root, o);
c[o][] = root, f[root] = o; root = o; f[o] = ; c[fa][] = child, f[child] = fa;
}
return ans;
}
int find_max() {
int o = *(--(--S.end())), fa = f[o], child = c[o][];
int ans = query(root, o);
if (o != root) {
cut(o, fa), cut(child, o), link(child, fa), link(root, o);
c[o][] = root, f[root] = o; root = o; f[o] = ; c[fa][] = child, f[child] = fa;
}
return ans;
}
int del_min() {
int o = *(++S.begin()), fa = f[o], child = c[o][];
int ans = query(root, o);
cut(o, fa), cut(o, child); link(fa, child);
f[child] = fa, c[fa][] = child;
S.erase(S.find(o)); f[o] = c[o][] = c[o][] = ;
if (root == o) root = child, f[child] = ;
return ans;
}
int del_max() {
int o = *(--(--S.end())), fa = f[o], child = c[o][];
int ans = query(root, o);
cut(o, fa), cut(o, child); link(fa, child);
f[child] = fa, c[fa][] = child;
S.erase(S.find(o)); f[o] = c[o][] = c[o][] = ;
if (root == o) root = child, f[child] = ;
return ans;
}
}T; void work() {
scanf("%d", &m);
for (int i = ; i <= m; i++) {
scanf("%d", &q[i].opt);
if (q[i].opt == ) {
scanf("%d", &q[i].x); a[++top] = q[i].x;
}
}
sort(a+, a++top); top = unique(a+, a+top+)-a-;
for (int i = ; i <= m; i++) {
if (q[i].opt == ) printf("%d\n", T.insert(lower_bound(a+, a+top+, q[i].x)-a));
else if (q[i].opt == ) printf("%d\n", T.find_min());
else if (q[i].opt == ) printf("%d\n", T.find_max());
else if (q[i].opt == ) printf("%d\n", T.del_min());
else printf("%d\n", T.del_max());
}
}
int main() {
work();
return ;
}
[HNOI 2017]单旋的更多相关文章
- bzoj4825 [Hnoi2017]单旋
Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...
- BZOJ:4825: [Hnoi2017]单旋
Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...
- bzoj 4825: [Hnoi2017]单旋 [lct]
4825: [Hnoi2017]单旋 题意:有趣的spaly hnoi2017刚出来我就去做,当时这题作死用了ett,调了5节课没做出来然后发现好像直接用lct就行了然后弃掉了... md用lct不知 ...
- 【BZOJ4825】【HNOI2017】单旋(Link-Cut Tree)
[BZOJ4825][HNOI2017]单旋(Link-Cut Tree) 题面 题面太长,懒得粘过来 题解 既然题目让你写Spaly 那就肯定不是正解 这道题目,让你求的是最大/最小值的深度 如果有 ...
- HNOI2017 单旋
题目描述 网址:https://www.luogu.org/problemnew/show/3721 大意: 有一颗单旋Splay(Spaly),以key值为优先度,总共有5个操作. [1] 插入一个 ...
- 「AHOI / HNOI2017」单旋
「AHOI / HNOI2017」单旋 题目链接 H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种 ...
- HNOI2017单旋
单旋 这道题做法贼多,LCT,splay,线段树什么的貌似都行. 像我这种渣渣只会线段树了(高级数据结构学了也不会用). 首先离线所有操作,因为不会有两个点值重复,所以直接离散. 一颗线段树来维护所有 ...
- P3721 [AH2017/HNOI2017]单旋
题目:https://www.luogu.org/problemnew/show/P3721 手玩一下即可AC此题. 结论:插入x后,x要么会成为x的前驱的右儿子,要么成为x的后继的左儿子,这取决于它 ...
- 洛谷P3721 单旋
什么毒瘤...... 题意:模拟一棵单旋splay,求每次插入,splay最值,删除最值的操作次数. 解:乍一看感觉很神,又因为是LCT题单上的,然后就折磨了我好久,最后跑去看题解... 居然是手玩找 ...
随机推荐
- Duplicate column name 'vocabulary'
创建一个视图: 报错:Duplicate column name 'vocabulary' 意思是视图select的列名重复了,取别名 改成这样就ok了
- ES5和ES6两个值的比较
ES5比较两个值是否相等 1)相等运算符 (==):比较两个数值是否相等,自动转换类型后再进行比较 2)全等运算符(===):比较两个比较值的数值和类型是否相等 ES5的特殊: ES6提出" ...
- Hibernate——配置并访问数据库
Hibernate,对于java来说很重要的一个东西,用于持久层.之前看了很多配置的,都不行,自己来写一个配置成功的. 环境:jdk1.8,eclipse-jee-oxygen,mysql-conne ...
- 听翁恺老师mooc笔记(12)--结构中的结构
结构数组: 和C语言中的int,double一样,一旦我们做出一个结构类型,就可以定义这个结构类型的变量,也可以定义这个结构类型的数组.比如下面这个例子: struct date dates[100] ...
- Java暑期作业
一.假期观影笔记--<熔炉> 影片<熔炉>是根据发生在韩国光州聋哑学校里的真实事件而改编.影片讲述的是在一所聋哑儿童学校里,校长.教务以及老师披着慈善的华丽外衣对学校中的多名未 ...
- php的数组的函数
1.可以将一个二位数组转化成两个一维数组,没有指定键就是默认的索引 注意二位数组有几种类型,其中最常见的一种是外层循环是一个索引数组,然后内层是一个关联数组.这种通过便利第一层,然后第二层指定关联词就 ...
- c 语言常量
1,整数常量 整数常量可以是十进制.八进制或十六进制的常量.前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制. 整数常量也可以带一个后缀,后缀是 U 和 L 的组合 ...
- URL编码和Base64编码 (转)
我们经常会遇到所谓的URL编码(也叫百分号编码)和Base64编码. 先说一下Bsae64编码.BASE64编码是一种常用的将二进制数据转换为64个可打印字符的编码,常用于在通常处理文本数据 ...
- Ubuntu16.04 + Zabbix 3.4.7 邮件报警设置
部署了Zabbix,需要配置邮件报警,在网上找了一些教程,大多是是用的CentOS + Zabbix 2.x版本的,而且还要写脚本,感觉太麻烦了,所以自己结合其他文章摸索了一套配置方法. 先说一下环境 ...
- java中类的三大特征之多态
Java 多态 同一种事物由于条件不同,展示出不同的结果,叫做多态. 父类的引用类型,由于使用不同的子类对象实例,而执行不同的操作. 多态存在的三个必要条件 1. 子类继承父类: 2. 子类重写父类方 ...