洛谷P3285 [SCOI2014]方伯伯的OJ 动态开点平衡树
洛谷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;
}