洛谷 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\),一开始他们按照编号排名.方伯伯会按照 ...
随机推荐
- javascript-原生-闭包
1.变量的作用域 前提:这里只全部都通过var创建的变量或对象 1.全局变量:函数外创建变量 var x=10; function test(){ alert("全局变量在test函数中&q ...
- win 常用修复蓝屏,系统比对最后更新20210804
您可以尝试以下方案: 在管理员命令提示符下键入以下命令:Dism /Online /Cleanup-Image /ScanHealth这条命令将扫描全部系统文件并和官方系统文件对比,扫描计算机中的不一 ...
- react 生命周期 个人见解
初始化/实例期 gitDefaultprops 获取组件的默认props状态 gitInitialstate 类定义方式或是直接在构造函数中挂载state componentWillMount 组件 ...
- 2021.8.21考试总结[NOIP模拟45]
T1 打表 由归纳法可以发现其实就是所有情况的总和. $\frac{\sum_{j=1}^{1<<k}(v_j-v_{ans})}{2^k}$ $code:$ 1 #include< ...
- 加法运算替代 牛客网 程序员面试金典 C++ Python
加法运算替代 牛客网 程序员面试金典 题目描述 请编写一个方法,实现整数的乘法.减法和除法运算(这里的除指整除).只允许使用加号. 给定两个正整数int a,int b,同时给定一个int type代 ...
- Django settings.py设置 DEBUG=False后静态文件无法加载解决
解决办法: settings.py 文件 DEBUG = False STATIC_ROOT = os.path.join(BASE_DIR,'static') #新增 urls.py文件(项目的) ...
- The art of multipropcessor programming 读书笔记-3. 自旋锁与争用(2)
本系列是 The art of multipropcessor programming 的读书笔记,在原版图书的基础上,结合 OpenJDK 11 以上的版本的代码进行理解和实现.并根据个人的查资料以 ...
- go条件语句
1. if else package main import "fmt" func main(){ a :=123 if a>100{ fmt.prinln("大于 ...
- Spring Cache 带你飞(二)
接着上一篇讲了 Spring Cache 如何被 Spring Aop 代理加载对应的代码,以及何如注入相关界面逻辑. Spring Cache 带你飞(一) 本篇我们围绕两个要点展开: 一个数据是如 ...
- css--元素居中常用方法总结
前言 元素居中是日常开发和学习中最常见的问题,同时也是面试中经常考察的知识点,本文来总结一下这方面的知识点. 正文 1.水平居中 (1)子父元素宽度固定,子元素设置 margin:auto,并且子元素 ...