洛谷题面传送门

在酒店写的,刚了一整晚终于调出来了……

首先考虑当 \(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(平衡树)的更多相关文章

  1. 洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树

    洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树 题目描述 方伯伯正在做他的 \(Oj\) .现在他在处理 \(Oj\) 上的用户排名问题. \(Oj\) 上注册了 \(n\) 个用户 ...

  2. 洛谷 P3285 [SCOI2014]方伯伯的OJ

    看到这题,第一眼:平衡树水题,随便做一做好了 然后....我在花了n个小时去调试(维护平衡树父节点)之后,... 调了三个小时后,第一次失败的代码(只能查找排名为k的用户编号,不能根据编号查排名) # ...

  3. 洛谷P3286 [SCOI2014]方伯伯的商场之旅

    题目:洛谷P3286 [SCOI2014]方伯伯的商场之旅 思路 数位DP dalao说这是数位dp水题,果然是我太菜了... 自己是不可能想出来的.这道题在讲课时作为例题,大概听懂了思路,简单复述一 ...

  4. luogu P3285 [SCOI2014]方伯伯的OJ splay 线段树

    LINK:方伯伯的OJ 一道稍有质量的线段树题目.不写LCT splay这辈子是不会单独写的 真的! 喜闻乐见的是 题目迷惑选手 \(op==1\) 查改用户在序列中的位置 题目压根没说位置啊 只有排 ...

  5. 洛谷 P3287 - [SCOI2014]方伯伯的玉米田(BIT 优化 DP)

    洛谷题面传送门 怎么题解区全是 2log 的做法/jk,这里提供一种 1log 并且代码更短(bushi)的做法. 首先考虑对于一个序列 \(a\) 怎样计算将其变成单调不降的最小代价.对于这类涉及区 ...

  6. 洛谷P3287 [SCOI2014]方伯伯的玉米田(树状数组)

    传送门 首先要发现,每一次选择拔高的区间都必须包含最右边的端点 为什么呢?因为如果拔高了一段区间,那么这段区间对于它的左边是更优的,对它的右边会更劣,所以我们每一次选的区间都得包含最右边的端点 我们枚 ...

  7. 洛谷3288 SCOI2014方伯伯运椰子(分数规划+spfa)

    纪念博客又一次爆炸了 首先,对于本题中,我们可以发现,保证存在正整数解,就表示一定费用会降低.又因为一旦加大的流量,费用一定会变大,所以总流量一定是不变的 那么我们这时候就需要考虑一个退流的过程 对于 ...

  8. BZOJ 3595: [Scoi2014]方伯伯的Oj SBT+可持久化Treap

    3595: [Scoi2014]方伯伯的Oj Time Limit: 6 Sec  Memory Limit: 256 MBSubmit: 102  Solved: 54[Submit][Status ...

  9. 洛谷 P3285 / loj 2212 [SCOI2014] 方伯伯的 OJ 题解【平衡树】【线段树】

    平衡树分裂钛好玩辣! 题目描述 方伯伯正在做他的 OJ.现在他在处理 OJ 上的用户排名问题. OJ 上注册了 \(n\) 个用户,编号为 \(1\sim n\),一开始他们按照编号排名.方伯伯会按照 ...

随机推荐

  1. python的参数传递是值传递还是引用传递?都不是!

    [写在前面] 参考文章: https://www.cnblogs.com/spring-haru/p/9320493.html[偏理论,对值传递和引用传递作了总结] https://www.cnblo ...

  2. perl打开读取文件(open)

    在Perl中可以用open或者sysopen函数来打开文件进行操作,这两个函数都需要通过一个文件句柄(即文件指针)来对文件进行读写定位等操作.下面以open函数为例:1:读:open(文件句柄,&qu ...

  3. 工厂模式--摆脱你日复一日new对象却依旧单身的苦恼!

    前言 每每谈及到Java,就不免会想到一个悲伤的事实:你是否每天都在new对象,却依然坚守在单身岗上屹立不倒.(所谓面向对象编程hhh),这篇来学一下工厂模式,摆脱new对象的苦恼! 知识点 传统工厂 ...

  4. [源码解析] PyTorch如何实现前向传播(3) --- 具体实现

    [源码解析] PyTorch如何实现前向传播(3) --- 具体实现 目录 [源码解析] PyTorch如何实现前向传播(3) --- 具体实现 0x00 摘要 0x01 计算图 1.1 图的相关类 ...

  5. js判断移动端浏览器类型,微信浏览器、支付宝小程序、微信小程序等

    起因 现在市场上各种跨平台开发方案百家争鸣各有千秋,个人认为最成熟的还是hybird方案,简单的说就是写H5各种嵌入,当然作为前端工程师最希望的也就是公司采用hybird方案当作技术路线. 所谓的hy ...

  6. .net Xml加密解密操作

    生成密钥的方法: /// <summary>生成RSA加密 解密的 密钥 /// 生成的key就是 方法EncryptByRSA与DecryptByRSA用的key了 /// </s ...

  7. Java:HashTable类小记

    Java:HashTable类小记 对 Java 中的 HashTable类,做一个微不足道的小小小小记 概述 public class Hashtable<K,V> extends Di ...

  8. JVM:GC Roots

    JVM:GC Roots 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 JVM 垃圾回收的时候如何确定垃圾 什么是垃圾 简单来说就是内存中已经不再被使用的空间就 ...

  9. BUAA_2020_软件工程_软件案例分析作业

    项目 内容 这个作业属于那个课程 班级博客 这个作业的要求在哪里 作业要求 我在这个课程的目标是 学习掌握软件工程的相关知识 这个作业在哪个具体方面帮我实现目标 通过对具体软件案例的分析学习软件工程 ...

  10. 主仆见证了 Hobo 的离别 题解

    前言: 题面挺神仙.反正我考试的时候看了40分钟也没看懂. 后来改题感觉自己写的挺假,没想到加个\(k==1\)的特判竟然就A了?无语力. 解析: 看懂题以后就好说了.首先这显然是一个树形结构.我们考 ...