【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,甚至出现负数,调啊调啊调啊调~~~ 只会旋转最大值和最小值,以最小 ...
随机推荐
- Matlab 中实用数据结构之 containers.Map
概要 熟悉 Python 的都知道字典 Dict 类型数据结构功能的强大,Matlab 中虽然有表结构,但是其列名必须是亦变量名类型的字符串,如果我想用数字开头的字符串作键值,其表结构就无能为力了 ...
- double类型的小数,四舍五入保留两位小数
import java.math.BigDecimal; public class Kewai{ public static void main(String[] args) { double f = ...
- bootstrap2文档的学习
就像刚开始的 优雅,直观,强大的前端框架,让web开发更快,更容易,bootstrap给我的感觉就是把常用的布局,组件(导航,列表,按钮,表格),还有规范化颜色等等,同时它的遍历不至于此,他还支持了自 ...
- 使用jquery实现获取除this(当前选定)以外的元素
今天做项目时,要求完成功能评价的分类,即好评,差评,中评.遇到一个问题,如何在选定一个评论类型时,该div颜色改变,其他评论类型的div颜色不变. 在使用$(this).attr()时,表示当前元素的 ...
- idea 创建springboot工程
公司最近用springboot做微服务开发 1,使用idea创建一个spring initializr 工程 2,点击next 3,配置好后继续next 4,可以勾选上web,继续next 5,fin ...
- Java程序设计第四次作业内容 第五次作业10月9号发布,为第三章全部例题
第六题:使用判断语句,根据数字,输出对应的中文是星期几? 直接使用一个if语句的情况 int weekDay=3; if(weekDay==1){ sop("今天是星期一"); } ...
- iOS 多线程(NSThread、GCD、NSOperation)
ios中得多线程技术主要使用3种:NSThread.NSOperation和GCD 一.NSThread: 最轻量级方法,但是不安全需要手动加锁,需要自己管理生命周期 NSThread的使用方法有2种 ...
- Django之视图和URL配置
1.在创建项目时,Django会自动创建URL配置,在urls.py文件中 文件的默认内容如下所示: """mysite URL Configuration The ur ...
- Python基础函数必学
我们知道圆的面积计算公式为: S = πr2 当我们知道半径r的值时,就可以根据公式计算出面积.假设我们需要计算3个不同大小的圆的面积: r1 = 12.34 r2 = 9.08 r3 = 73.1 ...
- Tufurama CodeForces - 961E
Tufurama CodeForces - 961E 题意:有一部电视剧有n季,每一季有ai集.问有多少对i,j存在第i季第j集也同时存在第j季第i集. 思路:核心问题还是统计对于第i季,你要统计第i ...