洛谷 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\),一开始他们按照编号排名.方伯伯会按照 ...
随机推荐
- MyBatis的框架设计
1.MyBatis的框架设计 2.整体设计 2.1 总体流程 (1)加载配置并初始化 触发条件:加载配置文件 配置来源于两个地方,一处是配置文件,一处是Java代码的注解,将SQL的配置信 ...
- 初学Python-day12 装饰器函数
装饰器 1.概念 本质就是一个Python函数,其他函数在本身不变的情况下去增加额外的功能,装饰器的返回值是一个函数. 常用的场景:插入日志,事务处理,缓存,权限校验等. 2.普通函数回顾 1 def ...
- .net 5.0 ref文件夹的作用
ref目录里的dll是一个名为参考组件的东西,微软MSDN给的解释是 参考组件是一种特殊类型的程序集,仅包含表示库的公共API面所需的最小元数据数量.它们包括用于在构建工具中引用程序集时重要的所有成员 ...
- BUAA_2019_OO_第一单元总结
一.基于度量来分析自己的程序结构 1.第一次作业 1.1类图: 第一次作业由于比较简单,我采用了面向过程的编程方式.在Polynomail类的构造函数中将项直接求导输出.这样的弊端显而易见,不能进行优 ...
- 用建造者模式实现一个防SQL注入的ORM框架
本文节选自<设计模式就该这样学> 1 建造者模式的链式写法 以构建一门课程为例,一个完整的课程由PPT课件.回放视频.课堂笔记.课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造 ...
- (六)、Docker 之 Dockerfile
1.什么是Dockerfile Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本. 2.Dockerfile解析过程 前提认知: 每条保留字指令都必须为大写字母 ...
- 生产环境部署springcloud微服务启动慢的问题排查
今天带来一个真实案例,虽然不是什么故障,但是希望对大家有所帮助. 一.问题现象: 生产环境部署springcloud应用,服务部署之后,有时候需要10几分钟才能启动成功,在开发测试环境则没有这个问题. ...
- allegro查看线宽的方法
- cf 11B Jumping Jack(贪心,数学证明一下,,)
题意: 给一个数X. 起始点为坐标0.第1步跳1格,第2步跳2格,第3步跳3格,.....以此类推. 每次可以向左跳或向右跳. 问最少跳几步可以到坐标X. 思路: 假设X是正数. 最快逼近X的方法是不 ...
- 面试题系列:工作5年,第一次这么清醒的理解final关键字?
面试题:用过final关键字吗?它有什么作用 面试考察点 考察目的: 了解面试者对Java基础知识的理解 考察人群: 工作1-5年,工作年限越高,对于基础知识理解的深度就越高. 背景知识 final关 ...