洛谷 P3285 - [SCOI2014]方伯伯的OJ(平衡树)
在酒店写的,刚了一整晚终于调出来了……
首先考虑当 \(n\) 比较小(\(10^5\) 级别)的时候怎么解决,我们考虑将所有用户按排名为关键字建立二叉排序树,我们同时再用一个 map 维护下编号为 \(x\) 的用户在原平衡树上对应的节点编号是什么。那么对于每次操作我们需进行的操作如下:
- \(1\) 类操作:直接在
map中找到 \(x\) 对应的节点编号,将该节点对应的用户编号改为 \(y\),同时更新map中用户编号为 \(y\) 对应的节点编号。 - \(2\) 类操作:在
map中找到 \(x\) 对应的节点编号 \(id\),然后将 \(id\) 从原平衡树中分离出来,然后将 \(id\) 与根节点合并,其中 \(id\) 排名小于根节点的排名。那么怎么实现分离这一操作呢?相较于普通的平衡树不同的一点是,这次我们对于每个点记录其父亲编号,然后假设我们要将 \(p\) 节点分离出来,那么我们就考察其父亲,如果其没有父亲我们就直接将根节点设为 \(p\) 左右儿子合并的结果,如果 \(p\) 是其父亲的左儿子(类似于 splay 里的 identify 函数)就将其父亲的左儿子设为 \(p\) 左右儿子合并的结果,否则将其父亲的右儿子设为 \(p\) 左右儿子合并的结果。同时将 \(p\) 左右儿子即父亲都设为空。 - \(3\) 类操作:与 \(2\) 类操作几乎一致,只不过这次我们将 \(id\) 合并到根节点后面。
- \(4\) 类操作:直接在平衡树上二分,然后输出对应节点用户编号。
接下来考虑原问题。注意到不同编号虽然很多,但是如果我们把编号连续排名也连续的这些编号合并起来,那么每次操作最多增加两个合并后的连续段,也就是说这个连续段的个数是 \(\mathcal O(m)\) 的,因此我们考虑平衡树上每个节点维护一个编号的连续段。同样地我们也可以用某种数据结构找到每个点所在连续段对应的节点编号,只不过由于此题用户个数很多,使用 map 逐一存储不可取,因此我们考虑建一个 set 并将所有连续段左端点及其编号看作一个二元组压入一个 set,查询在 set 中二分即可。同时由于每次修改可能会增加新的连续段,因此我们要将一个节点裂成两个,具体来说假设我们要将 \([L,R]\) 从中间 \(p\) 处断开,那么我们就新建两个节点表示 \([L,p-1]\) 和 \([p+1,R]\),然后将 \([L,p-1]\) 放在该节点左边,\([p+1,R]\) 放在该节点右边,原节点编号区间改为 \([p,p]\) 即可。注意这里“将 \([L,p-1]\) 挂在该节点左边”不能直接简简单单地将 \([L,p-1]\) 设为原节点的左儿子,同时将原来该节点的左儿子挂在 \([L,p-1]\) 的左儿子处,而要将 \([L,p-1]\) 与该节点原来的左儿子做一遍 merge 操作,否则复杂度会退化。
时间复杂度 \(m\log m\)。
const int MAXM=2e5;
int n,qu;
struct node{
int sum,val,key,st,ch[2],f;
node(int _sum=0,int _val=0,int _st=0){
sum=_sum;val=_val;st=_st;key=rand();
ch[0]=ch[1]=f=0;
}
} s[MAXM+5];
int rt=1,ncnt=1;set<pii> nds;
void pushup(int k){s[k].sum=s[s[k].ch[0]].sum+s[s[k].ch[1]].sum+s[k].val;}
void setson(int k,int c,int v){s[v].f=k;s[k].ch[c]=v;}
int get(int x){pii p=*--nds.upper_bound(mp(x,INF));return p.se;}
int merge(int x,int y){
if(!x||!y) return x+y;
if(s[x].key<s[y].key) return setson(x,1,merge(s[x].ch[1],y)),pushup(x),x;
else return setson(y,0,merge(x,s[y].ch[0])),pushup(y),y;
}
void split_nd(int k,int p){
// printf("split %d %d\n",k,p);
int L=s[k].st,R=s[k].st+s[k].val-1;
if(L==R) return;
nds.erase(nds.find(mp(L,k)));
if(p!=L){
int ls=++ncnt;s[ls]=node(p-L,p-L,L);
nds.insert(mp(L,ls));setson(k,0,merge(s[k].ch[0],ls));
} if(p!=R){
int rs=++ncnt;s[rs]=node(R-p,R-p,p+1);
nds.insert(mp(p+1,rs));setson(k,1,merge(rs,s[k].ch[1]));
} s[k].val=1;s[k].st=p;nds.insert(mp(p,k));
}
int query(int sz){
int k=rt;
while(1){
// printf("%d %d %d\n",k,sz,s[k].st);
if(sz<=s[s[k].ch[0]].sum) k=s[k].ch[0];
else if(sz>s[s[k].ch[0]].sum+s[k].val) sz-=s[s[k].ch[0]].sum+s[k].val,k=s[k].ch[1];
else return s[k].st+sz-s[s[k].ch[0]].sum-1;
}
}
void print(int k){
if(!k) return;print(s[k].ch[0]);
printf("node %d [%d,%d] %d %d %d %d\n",k,s[k].st,s[k].st+s[k].val-1,s[k].sum,s[k].f,s[k].ch[0],s[k].ch[1]);
print(s[k].ch[1]);
}
int walk(int k){
// printf("walk %d\n",k);print(rt);
int res=1+s[s[k].ch[0]].sum,pr=k;
while(k){
k=s[k].f;//printf("%d\n",k);
if(pr==s[k].ch[1]) res+=s[s[k].ch[0]].sum,res+=s[k].val;
pr=k;
} return res;
}
int main(){
scanf("%d%d",&n,&qu);srand(20211005203353);
s[1]=node(n,n,1);nds.insert(mp(1,1));int pre=0;
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){
int x,y;scanf("%d%d",&x,&y);
x-=pre;y-=pre;int pt=get(x);
split_nd(pt,x);s[pt].st=y;
nds.erase(nds.find(mp(x,pt)));
nds.insert(mp(y,pt));
printf("%d\n",pre=walk(pt));
} else if(opt==2){
int x;scanf("%d",&x);x-=pre;
int pt=get(x);split_nd(pt,x);
printf("%d\n",pre=walk(pt));
int nd=merge(s[pt].ch[0],s[pt].ch[1]);
s[pt].ch[0]=s[pt].ch[1]=0;pushup(pt);
if(s[pt].f){
int fa=s[pt].f;
if(s[fa].ch[0]==pt) setson(fa,0,nd);
else setson(fa,1,nd);
} else rt=nd,s[rt].f=0;
for(int j=s[pt].f;j;j=s[j].f) pushup(j);
s[pt].f=0;
rt=merge(pt,rt);
} else if(opt==3){
int x;scanf("%d",&x);x-=pre;
int pt=get(x);split_nd(pt,x);
printf("%d\n",pre=walk(pt));
int nd=merge(s[pt].ch[0],s[pt].ch[1]);
s[pt].ch[0]=s[pt].ch[1]=0;pushup(pt);
if(s[pt].f){
int fa=s[pt].f;
if(s[fa].ch[0]==pt) setson(fa,0,nd);
else setson(fa,1,nd);
} else rt=nd,s[rt].f=0;
for(int j=s[pt].f;j;j=s[j].f) pushup(j);
s[pt].f=0;
rt=merge(rt,pt);
} else {
int k;scanf("%d",&k);k-=pre;
printf("%d\n",pre=query(k));
}
}
return 0;
}
洛谷 P3285 - [SCOI2014]方伯伯的OJ(平衡树)的更多相关文章
- 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树
洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...
- 洛谷 P3285 [SCOI2014]方伯伯的OJ
看到这题,第一眼:平衡树水题,随便做一做好了 然后....我在花了n个小时去调试(维护平衡树父节点)之后,... 调了三个小时后,第一次失败的代码(只能查找排名为k的用户编号,不能根据编号查排名) # ...
- 洛谷P3286 [SCOI2014]方伯伯的商场之旅
题目:洛谷P3286 [SCOI2014]方伯伯的商场之旅 思路 数位DP dalao说这是数位dp水题,果然是我太菜了... 自己是不可能想出来的.这道题在讲课时作为例题,大概听懂了思路,简单复述一 ...
- luogu P3285 [SCOI2014]方伯伯的OJ splay 线段树
LINK:方伯伯的OJ 一道稍有质量的线段树题目.不写LCT splay这辈子是不会单独写的 真的! 喜闻乐见的是 题目迷惑选手 \(op==1\) 查改用户在序列中的位置 题目压根没说位置啊 只有排 ...
- 洛谷 P3287 - [SCOI2014]方伯伯的玉米田(BIT 优化 DP)
洛谷题面传送门 怎么题解区全是 2log 的做法/jk,这里提供一种 1log 并且代码更短(bushi)的做法. 首先考虑对于一个序列 \(a\) 怎样计算将其变成单调不降的最小代价.对于这类涉及区 ...
- 洛谷P3287 [SCOI2014]方伯伯的玉米田(树状数组)
传送门 首先要发现,每一次选择拔高的区间都必须包含最右边的端点 为什么呢?因为如果拔高了一段区间,那么这段区间对于它的左边是更优的,对它的右边会更劣,所以我们每一次选的区间都得包含最右边的端点 我们枚 ...
- 洛谷3288 SCOI2014方伯伯运椰子(分数规划+spfa)
纪念博客又一次爆炸了 首先,对于本题中,我们可以发现,保证存在正整数解,就表示一定费用会降低.又因为一旦加大的流量,费用一定会变大,所以总流量一定是不变的 那么我们这时候就需要考虑一个退流的过程 对于 ...
- BZOJ 3595: [Scoi2014]方伯伯的Oj SBT+可持久化Treap
3595: [Scoi2014]方伯伯的Oj Time Limit: 6 Sec Memory Limit: 256 MBSubmit: 102 Solved: 54[Submit][Status ...
- 洛谷 P3285 / loj 2212 [SCOI2014] 方伯伯的 OJ 题解【平衡树】【线段树】
平衡树分裂钛好玩辣! 题目描述 方伯伯正在做他的 OJ.现在他在处理 OJ 上的用户排名问题. OJ 上注册了 \(n\) 个用户,编号为 \(1\sim n\),一开始他们按照编号排名.方伯伯会按照 ...
随机推荐
- 自动化运维利器Ansible要点汇总
由于大部分互联网公司服务器环境复杂,线上线下环境.测试正式环境.分区环境.客户项目环境等造成每个应用都要重新部署,而且服务器数量少则几十台,多则千台,若手工一台台部署效率低下,且容易出错,不利后期运维 ...
- 工厂模式--摆脱你日复一日new对象却依旧单身的苦恼!
前言 每每谈及到Java,就不免会想到一个悲伤的事实:你是否每天都在new对象,却依然坚守在单身岗上屹立不倒.(所谓面向对象编程hhh),这篇来学一下工厂模式,摆脱new对象的苦恼! 知识点 传统工厂 ...
- Stream中的Collector收集器原理
前言 Stream的基本操作因为平时工作中用得非常多(也能看到一些同事把Stream操作写得很丑陋),所以基本用法就不写文章记录了. 之所以能把Stream的操作写得很丑陋,完全是因为Stream底层 ...
- Takin Talks·上海 |开源后首场主题研讨会来了,一起解密Takin技术吧!
自 6 月 25 日全球首款生产环境全链路压测平台 Takin 正式开源,短短 13 天时间,Github 主页上 Star 数已超过 730,开发者社群也积累了 1500+粉丝.群内技术研讨氛围 ...
- Redis:学习笔记-01
Redis:学习笔记-01 该部分内容,参考了 bilibili 上讲解 Redis 中,观看数最多的课程 Redis最新超详细版教程通俗易懂,来自 UP主 遇见狂神说 1. Redis入门 2.1 ...
- 注解,@Qualifier+@Autowired 和 @Resource
摘要: 项目中,对于AOP的使用,就是通过用注解来注入的. 更改之前的注解,是使用:@Qualifier+@Autowired 但是,通过这样注解,在项目启动阶段,需要自动扫描的过程是非常缓慢的, ...
- [no_code]OCR表格处理——功能规格说明书
项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 功能规格说明书 我们在这个课程的目标是 远程协同工作,采用最新技术开发软件 这个作业在哪个具体方面 ...
- 2021.8.14考试总结[NOIP模拟39]
T1 打地鼠 全场就俩人没切,还有一个是忘关$freopen$了. $code:$ 1 #include<bits/stdc++.h> 2 #define rin register sig ...
- lib库无法加载的情况分析
最近升级vs2017的时候遇到无法加载库的问题,在网上查找问题,网上给出可能有三种情况导致该问题:路径是否正确:库依赖是否齐全:库版本是否正确.最直接的方法就是用depends软件去查询,是否有模块有 ...
- stm32中的串口通信你了解多少
在基础实验成功的基础上,对串口的调试方法进行实践.硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中. b) 初始化函数定义: void USART_Confi ...