【bzoj4825】[Hnoi2017]单旋 线段树+STL-set
题目描述

输入
输出
样例输入
5
1 2
1 1
1 3
4
5
样例输出
1
2
2
2
2
题解
Splay 线段树+STL-set
这道题的关键之处在于:除了插入以外,只操作最大/最小值。
所以每次旋转只有一个固定方向。
以旋转最小值为例,一定是不断的进行zig操作最终旋转到根。
通过找规律可以证明这种操作之后,最小节点深度变为1,原来最小节点的右子树深度不变,其余节点深度+1并且结构不变。
而右子树一定是一段连续的区间,这样我们可以使用线段树维护这个过程,同时还要维护每个节点的父亲和儿子,这个不难维护。
删除的时候,把根节点删掉,其余节点深度-1且结构不变。
这样2345操作都搞定了,只剩下1操作。
考虑到插入一个数x,一定是在它的前驱pro或后继sub其一插入,而且一定存在某个节点不存在相应的儿子。
具体地,有个结论:pro和sub的深度一定不同,且深度大的可以插入。
具体证明很简单,在这里不细讲了。
这样只要知道前驱后继即可,可以在线段树上求,但我懒了直接上set搞定。
#include <cstdio>
#include <algorithm>
#include <set>
#define N 100010
#define lson l , mid , x << 1
#define rson mid + 1 , r , x << 1 | 1
using namespace std;
set<int> s;
set<int>::iterator it , pro , sub;
int sum[N << 2] , si[N << 2] , tag[N << 2] , ls[N] , rs[N] , fa[N] , opt[N] , a[N] , v[N] , tot , root;
void pushup(int x)
{
sum[x] = sum[x << 1] + sum[x << 1 | 1] , si[x] = si[x << 1] + si[x << 1 | 1];
}
void pushdown(int x)
{
if(tag[x])
{
sum[x << 1] += si[x << 1] * tag[x] , tag[x << 1] += tag[x];
sum[x << 1 | 1] += si[x << 1 | 1] * tag[x] , tag[x << 1 | 1] += tag[x];
tag[x] = 0;
}
}
void change(int p , int o , int d , int l , int r , int x)
{
if(l == r)
{
si[x] += o , sum[x] += d;
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(p <= mid) change(p , o , d , lson);
else change(p , o , d , rson);
pushup(x);
}
void update(int b , int e , int a , int l , int r , int x)
{
if(b <= l && r <= e)
{
sum[x] += a * si[x] , tag[x] += a;
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if(b <= mid) update(b , e , a , lson);
if(e > mid) update(b , e , a , rson);
pushup(x);
}
int query(int p , int l , int r , int x)
{
if(l == r) return sum[x];
pushdown(x);
int mid = (l + r) >> 1;
if(p <= mid) return query(p , lson);
else return query(p , rson);
}
int main()
{
int m , i , d;
scanf("%d" , &m);
for(i = 1 ; i <= m ; i ++ )
{
scanf("%d" , &opt[i]);
if(opt[i] == 1) scanf("%d" , &a[i]) , v[++tot] = a[i];
}
sort(v + 1 , v + tot + 1);
for(i = 1 ; i <= m ; i ++ ) if(opt[i] == 1) a[i] = lower_bound(v + 1 , v + tot + 1 , a[i]) - v;
for(i = 1 ; i <= m ; i ++ )
{
if(opt[i] == 1)
{
if(s.empty()) root = a[i] , d = 1;
else
{
it = s.upper_bound(a[i]) , sub = it;
if(sub == s.begin()) ls[*sub] = a[i] , fa[a[i]] = *sub , d = query(*sub , 1 , tot , 1) + 1;
else
{
pro = --it;
if(sub == s.end()) rs[*pro] = a[i] , fa[a[i]] = *pro , d = query(*pro , 1 , tot , 1) + 1;
else if(query(*sub , 1 , tot , 1) < query(*pro , 1 , tot , 1)) rs[*pro] = a[i] , fa[a[i]] = *pro , d = query(*pro , 1 , tot , 1) + 1;
else ls[*sub] = a[i] , fa[a[i]] = *sub , d = query(*sub , 1 , tot , 1) + 1;
}
}
printf("%d\n" , d);
change(a[i] , 1 , d , 1 , tot , 1);
s.insert(a[i]);
}
else if(opt[i] % 2 == 0)
{
it = s.begin() , d = query(*it , 1 , tot , 1);
printf("%d\n" , d);
if(*it != root)
{
update(1 , tot , 1 , 1 , tot , 1);
update(*it , *it , -d , 1 , tot , 1);
int t = fa[*it];
ls[t] = fa[*it] = 0;
if(rs[*it]) update(*it + 1 , t - 1 , -1 , 1 , tot , 1) , fa[rs[*it]] = t , ls[t] = rs[*it];
rs[*it] = root , fa[root] = *it , root = *it;
}
if(opt[i] == 4) root = rs[*it] , rs[*it] = fa[root] = 0 , change(*it , -1 , -1 , 1 , tot , 1) , update(1 , tot , -1 , 1 , tot , 1) , s.erase(*it);
}
else
{
it = s.end() , it -- , d = query(*it , 1 , tot , 1);
printf("%d\n" , d);
if(*it != root)
{
update(1 , tot , 1 , 1 , tot , 1);
update(*it , *it , -d , 1 , tot , 1);
int t = fa[*it];
rs[t] = fa[*it] = 0;
if(ls[*it]) update(t + 1 , *it - 1 , -1 , 1 , tot , 1) , fa[ls[*it]] = t , rs[t] = ls[*it];
ls[*it] = root , fa[root] = *it , root = *it;
}
if(opt[i] == 5) root = ls[*it] , ls[*it] = fa[root] = 0 , change(*it , -1 , -1 , 1 , tot , 1) , update(1 , tot , -1 , 1 , tot , 1) , s.erase(*it);
}
}
return 0;
}
【bzoj4825】[Hnoi2017]单旋 线段树+STL-set的更多相关文章
- [BZOJ4825][HNOI2017]单旋(线段树+Splay)
4825: [Hnoi2017]单旋 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 667 Solved: 342[Submit][Status][ ...
- 【BZOJ4825】[Hnoi2017]单旋 线段树+set
[BZOJ4825][Hnoi2017]单旋 Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能 ...
- BZOJ.4825.[AHOI/HNOI2017]单旋(线段树)
BZOJ LOJ 洛谷 这题不难啊,我怎么就那么傻,拿随便一个节点去模拟.. 我们只需要能够维护,将最小值或最大值转到根.模拟一下发现,对于最小值,它的右子树深度不变(如果存在),其余节点深度全部\( ...
- 洛谷P3721 [AH2017/HNOI2017]单旋(线段树 set spaly)
题意 题目链接 Sol 这题好毒瘤啊.. 首先要观察到几个性质: 将最小值旋转到根相当于把右子树变为祖先的左子树,然后将原来的根变为当前最小值 上述操作对深度的影响相当于右子树不变,其他的位置-1 然 ...
- BZOJ4825 [Hnoi2017]单旋 【线段树】
题目链接 BZOJ4825 题解 手模一下操作,会发现一些很优美的性质: 每次旋到根,只有其子树深度不变,剩余点深度\(+1\) 每次旋到根,[最小值为例]右儿子接到其父亲的左儿子,其余点形态不改变, ...
- bzoj4825 [Hnoi2017]单旋
Description H 国是一个热爱写代码的国家,那里的人们很小去学校学习写各种各样的数据结构.伸展树(splay)是一种数据结构,因为代码好写,功能多,效率高,掌握这种数据结构成为了 H 国的必 ...
- BZOJ4825: [Hnoi2017]单旋(Splay)
题面 传送门 题解 调了好几个小时--指针太难写了-- 因为只单旋最值,我们以单旋\(\min\)为例,那么\(\min\)是没有左子树的,而它旋到根之后,它的深度变为\(1\),它的右子树里所有节点 ...
- [BZOJ4825][HNOI2017]单旋spaly
BZOJ Luogu 题目太长了,就不放了. 题解 首先声明一点,无论是splay还是spaly,插入一个新的元素,都要rotate到根!所以说题目也算是给了一个错误示范吧. 我们发现把最值旋转到根并 ...
- 4825: [Hnoi2017]单旋
4825: [Hnoi2017]单旋 链接 分析: 以后采取更保险的方式写代码!!!81行本来以为不特判也可以,然后就总是比答案大1,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...
随机推荐
- POJ 2184 Cow Exhibition(背包)
希望Total Smart和Totol Funess都尽量大,两者之间的关系是鱼和熊掌.这种矛盾和背包的容量和价值相似. dp[第i只牛][j = 当前TotS] = 最大的TotF. dp[i][j ...
- Spark性能优化——和shuffle搏斗
Spark的性能分析和调优很有意思,今天再写一篇.主要话题是shuffle,当然也牵涉一些其他代码上的小把戏. 以前写过一篇文章,比较了几种不同场景的性能优化,包括portal的性能优化,web se ...
- 【BZOJ2730】[HNOI2012] 矿场搭建(找割点)
点此看题面 大致题意: 一张无向图,要求你在去掉任意一个节点之后,剩余的每个节点都能到达一个救援出口,问至少需要几个救援出口. 第一步:\(Tarjan\)求割点 首先,我们要跑一遍\(Tarjan\ ...
- python_63_装饰器6
#decorator意思:1.装饰器 2.语法糖 import time user,passwd='qi','123' def auth(func): def wrappper(*args, **kw ...
- Jquery-EasyUI combobox下拉框使用
制作一个json文件: <input data-options="url:'${pageContext.request.contextPath }/json/combobox_data ...
- SQL初次接触
1.SQL对大小写不敏感 2.部分SQL数据库要求结尾分号 3.分为两种DML(数据操作语言)和DDL(数据定义语言) sql中一些注意要点 1.设置主键 一般会在一个数据内设置一个主键(名字通常为i ...
- jsp中的文件上传
首先需要有以下的jar包 jsp代码如下: <!-- ${pageContext.request.contextPath}为: "/" + 当前项目名 --> < ...
- 数据追踪系统Zipkin 及其 Zipkin的php客户端驱动hoopak
Zipkin是Twitter的一个开源项目,是一个致力于收集Twitter所有服务的监控数据的分布式跟踪系统,它提供了收集数据,和查询数据两大接口服务.Zipkin 是一款开源的分布式实时数据追踪系统 ...
- PHP 批量操作 Excel
自己封装了一个批量操作excel文件的方法,通过xls文件地址集合遍历,第三个参数传入一个匿名函数用于每个需求的不同进行的操作,实例中我想要得到列表中含有折字的行,封装成sql语句返回. xls文件超 ...
- 学习Pytbon第八天,文件的操作
文件的常用操作字符 data=open('月亮代表我的心',encoding='utf-8').read() f=open('月亮代表我的心',encoding='utf-8')#提取内存对象也叫文件 ...