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

题目描述

方伯伯正在做他的 \(Oj\) 。现在他在处理 \(Oj\) 上的用户排名问题。 \(Oj\) 上注册了 \(n\) 个用户,编号为 \(1 \sim n\),一开始他们按照编号排名。

方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号:

\(1\).操作格式为 \(1\ x\ y\),意味着将编号为$ x$ 的用户编号改为 \(y\) ,而排名不变,执行完该操作后需要输出该用户在队列中的位置,数据保证 \(x\) 必然出现在队列中,同时,\(y\) 是一个当前不在排名中的编号。

\(2\).操作格式为\(2\ x\),意味着将编号为 \(x\) 的用户的排名提升到第一位,执行完该操作后需要输出执行该操作前编号为 \(x\) 用户的排名。

\(3\).操作格式为 \(3\ x\) ,意味着将编号为 \(x\) 的用户的排名降到最后一位,执行完该操作后需要输出执行该操作前编号为 \(x\) 用户的排名。

4.操作格式为 \(4\ k\),意味着查询当前排名为 \(k\) 的用户编号,执行完该操作后需要输出当前操作用户的编号。

但同时为了防止别人监听自己的工作,方伯伯对他的操作进行了加密,即将四种操作的格式分别改为了:

  • \(1\ x+a\ y+a\)
  • \(2\ x+a\)
  • \(3\ x+a\)
  • \(4\ k+a\)

其中 \(a\) 为上一次操作得到的输出,一开始 \(a=0\)。

例如:上一次操作得到的输出是 \(5\) 这一次操作的输入为:\(1\ 13\ 15\)

因为这个输入是经过加密后的,所以你应该处理的操作是 \(1\ 8\ 10\)

现在你截获了方伯伯的所有操作,希望你能给出结果。

输入输出格式

输入格式

输入的第 \(1\) 行包含 \(2\) 个用空格分隔的整数 \(n\) 和 \(m\) ,表示初始用户数和操作数。此后有 \(m\) 行,每行是一个询问,询问格式如上所示。

输出格式

输出包含 \(m\) 行。每行包含一个整数,其中第 \(i\) 行的整数表示第 \(i\) 个操作的输出。

输入输出样例

输入样例 #1

10 10

1 2 11

3 13

2 5

3 7

2 8

2 10

2 11

3 14

2 18

4 9

输出样例 #1

2

2

2

4

3

5

5

7

8

11

说明

对于 \(100\%\) 的数据,\(1 \leq n \leq 10^8\),\(1 \leq m \leq 10^5\)

输入保证对于所有的操作 \(1\),\(2\),\(3\),\(x\) 必然已经出现在队列中,同时对于所有操作 \(1\),\(1 <= y <= 2 \times 10^8\),并且 \(y\) 没有出现在队列中。

对于所有操作 \(4\),保证 \(1 \leq k \leq n\)。

分析

无旋 \(treap\) 按照 \(siz\) 分裂可以维护序列上的信息

暴力的做法是把 \(1 \sim n\) 中的每一个数都扔到平衡树中

对于每一个数,用一个数组记录一下它在平衡树上对应的是哪一个节点

对于操作 \(1\),只需要把 \(x\) 在平衡树上所代表的节点分裂出来,然后把当前节点的权值改为 \(y\),再合并回去即可

对于操作 \(2\),通过一个函数 \(getrk(x)\) 查找平衡树上编号为 \(x\) 的节点在序列中的位置

具体的实现就是一直跳父亲,如果当前节点是父亲节点的右儿子,那么将左儿子和父亲节点的 \(siz\) 累加

父亲的信息在 \(pushup\) 的时候更新,而且在进入 \(merge\) 和 \(split\) 的时候要把当前节点的父亲节点置为 \(0\)

找到排名之后把当前的节点和排名在它前面的节点都 \(split\) 出来,交换一下位置再合并回去即可

操作 \(3\) 同理

对于操作 \(4\),从根节点开始在平衡树上二分查找即可

但是 \(n\) 的范围达到了 \(1e8\),这样做复杂度肯定不对

发现 \(m\) 的范围比较小

也就是说有一些位置是不会被操作的,而且这样的位置很多

所以我们可以改变平衡树中维护的信息

将维护一个节点改为维护一个区间,可以理解为动态开点

节点上要多维护区间的左右端点以及长度

当对于某一个点进行操作时,就把当前节点所在的区间分成几个小区间扔进平衡树中

同时要开一个 \(map\) 记录一下每一个节点掌管的是哪一个区间

方便对于一个数快速查询它被哪一个节点所掌管

具体实现的过程中还是有很多细节的

代码

#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<ctime>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int cnt,rt,rt1,rt2,rt3,rt4,n,m;
std::map<int,int> mp;
#define mit std::map<int,int>::iterator
struct trr{
int ch[2],siz,rd,fa,l,r,len;
}tr[maxn];
void push_up(rg int da){
tr[da].siz=tr[tr[da].ch[0]].siz+tr[tr[da].ch[1]].siz+tr[da].len;
tr[tr[da].ch[0]].fa=da,tr[tr[da].ch[1]].fa=da;
}
int ad(rg int l,rg int r){
tr[++cnt].rd=rand();
mp[r]=cnt;
tr[cnt].l=l;
tr[cnt].r=r;
tr[cnt].len=r-l+1;
tr[cnt].siz=tr[cnt].len;
return cnt;
}
void split(rg int now,rg int val,rg int& x,rg int& y){
tr[now].fa=0;
if(!now){
x=y=0;
return;
}
if(tr[tr[now].ch[0]].siz+tr[now].len<=val){
x=now;
split(tr[now].ch[1],val-tr[tr[now].ch[0]].siz-tr[now].len,tr[now].ch[1],y);
} else {
y=now;
split(tr[now].ch[0],val,x,tr[now].ch[0]);
}
push_up(now);
}
int bing(rg int aa,rg int bb){
tr[aa].fa=0,tr[bb].fa=0;
if(!aa || !bb) return aa+bb;
if(tr[aa].rd<tr[bb].rd){
tr[aa].ch[1]=bing(tr[aa].ch[1],bb);
push_up(aa);
return aa;
} else {
tr[bb].ch[0]=bing(aa,tr[bb].ch[0]);
push_up(bb);
return bb;
}
}
int getrk(rg int da){
rg int nans=tr[tr[da].ch[0]].siz+tr[da].len;
while(tr[da].fa){
if(tr[tr[da].fa].ch[1]==da){
da=tr[da].fa;
nans+=tr[tr[da].ch[0]].siz+tr[da].len;
} else {
da=tr[da].fa;
}
}
return nans;
}
int kth(rg int da,rg int k){
while(1){
if(k>tr[tr[da].ch[0]].siz+tr[da].len){
k-=tr[tr[da].ch[0]].siz+tr[da].len;
da=tr[da].ch[1];
} else if(k<=tr[tr[da].ch[0]].siz+tr[da].len && k>=tr[tr[da].ch[0]].siz+1){
return da;
} else {
da=tr[da].ch[0];
}
}
}
int latans;
int main(){
srand(time(0));
n=read(),m=read();
rt=ad(1,n);
rg int aa,bb,cc,dd,ee;
rg mit it;
for(rg int i=1;i<=m;i++){
aa=read(),bb=read();
bb-=latans;
if(aa==1){
cc=read();
cc-=latans;
it=mp.lower_bound(bb);
dd=it->second;
ee=getrk(dd);
mp.erase(it);
split(rt,ee-1,rt,rt1);
split(rt1,tr[dd].len,rt1,rt2);
if(tr[dd].l!=bb) rt=bing(rt,ad(tr[dd].l,bb-1));
rt=bing(rt,ad(cc,cc));
if(tr[dd].r!=bb) rt=bing(rt,ad(bb+1,tr[dd].r));
rt=bing(rt,rt2);
printf("%d\n",latans=getrk(mp[cc]));
} else if(aa==2){
it=mp.lower_bound(bb);
dd=it->second;
ee=getrk(dd);
mp.erase(it);
printf("%d\n",latans=ee-(tr[dd].r-bb));
split(rt,ee-1,rt,rt1);
split(rt1,tr[dd].len,rt1,rt2);
rt=bing(ad(bb,bb),rt);
if(tr[dd].l!=bb) rt=bing(rt,ad(tr[dd].l,bb-1));
if(tr[dd].r!=bb) rt=bing(rt,ad(bb+1,tr[dd].r));
rt=bing(rt,rt2);
} else if(aa==3){
it=mp.lower_bound(bb);
dd=it->second;
ee=getrk(dd);
mp.erase(it);
printf("%d\n",latans=ee-(tr[dd].r-bb));
split(rt,ee-1,rt,rt1);
split(rt1,tr[dd].len,rt1,rt2);
if(tr[dd].l!=bb) rt=bing(rt,ad(tr[dd].l,bb-1));
if(tr[dd].r!=bb) rt=bing(rt,ad(bb+1,tr[dd].r));
rt=bing(rt,rt2);
rt=bing(rt,ad(bb,bb));
} else {
dd=kth(rt,bb);
ee=getrk(dd);
printf("%d\n",latans=tr[dd].r-(ee-bb));
}
}
return 0;
}

洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树的更多相关文章

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

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

  2. 洛谷 P3285 - [SCOI2014]方伯伯的OJ(平衡树)

    洛谷题面传送门 在酒店写的,刚了一整晚终于调出来了-- 首先考虑当 \(n\) 比较小(\(10^5\) 级别)的时候怎么解决,我们考虑将所有用户按排名为关键字建立二叉排序树,我们同时再用一个 map ...

  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. hdu 6827 Road To The 3rd Building

    题意: t组输入,每一组一个n,然后后面是n个树的值(我们放到数组v里面),你需要从[1,n]这个区间内挑选出来两个数i,j,你需要保证i<=j,之后你要求一下v[i]+v[i+1]+...+v ...

  2. AcWing 238.银河英雄传说 (边带权并查集)

    题意:有\(n\)列,有\(T\)条指令,若指令格式为\(M\),则将第\(i\)号的所有战舰移到第\(j\)号所在列的后面,若指令格式为\(C\),询问\(i\)和\(j\)是否在同一列,如果在,问 ...

  3. 【原创】k8s之job和Cronjob

    1.失败任务 apiVersion: batch/v1 kind: Job metadata: name: bad spec: template: metadata: name: bad spec: ...

  4. 4.安装etcdkeeper查看etcd数据库中的数据

    作者 微信:tangy8080 电子邮箱:914661180@qq.com 更新时间:2019-06-24 12:47:59 星期一 欢迎您订阅和分享我的订阅号,订阅号内会不定期分享一些我自己学习过程 ...

  5. MySQL数据库系列(四)- InnoDB下的共享表空间和独立表空间详解

    一.概念 共享表空间: Innodb的所有数据保存在一个单独的表空间里面,而这个表空间可以由很多个文件组成,一个表可以跨多个文件存在,所以其大小限制不再是文件大小的限制,而是其自身的限制.从Innod ...

  6. Leetcode(15)-三数之和

    给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组. 注意:答案中不可以包含重复的三元组. ...

  7. redis持久化-AOF

    1.aof文件写入与同步 2.aof重写 重写的目的是为了减小aof文件的体积,redis服务器可以创建一个新的aof文件来代替现有的aof文件,新文件不会有冗余的命令. BGREWRITEAOF:遍 ...

  8. React 性能优化 All In One

    React 性能优化 All In One Use CSS Variables instead of React Context https://epicreact.dev/css-variables ...

  9. Online analog video interview

    Online analog video interview 在线模拟视频面试 English 口语 https://www.pramp.com/#/ https://www.pramp.com/faq ...

  10. macOS finder show hidden files

    macOS finder show hidden files 显示 MacOS 上的隐藏文件和文件夹 https://zh.wikihow.com/显示Mac-OS-X上的隐藏文件和文件夹 $ def ...