https://www.luogu.org/problemnew/show/P3285
题目描述
方伯伯正在做他的Oj。现在他在处理Oj上的用户排名问题。Oj上注册了n个用户,编号为1~n“,一开始他们按照编号排名。
方伯伯会按照心情对这些用户做以下四种操作,修改用户的排名和编号:
1.操作格式为1 x y,意味着将编号为x的用户编号改为y,而排名不变,执行完该操作后需要输出该用户在队列中的位置,数据保证x必然出现在队列中,同时,1是一个当前不在排名中的编号。
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个操作的输出。
输入输出样例
10 10 1 2 11 3 13 2 5 3 7 2 8 2 10 2 11 3 14 2 18 4 9
2 2 2 4 3 5 5 7 8 11
说明
对于 100% 的数据,1 <= n <= 10^8,1 <= m <= 10^5
输入保证对于所有的操作 1,2,3,x 必然已经出现在队列中,同时对于所有操作 1,1 <= y <= 2 * 10^8,并且
y 没有出现在队列中。
对于所有操作 4,保证 1 <= k <= n。
动态开点线段树?
利用线段树维护序列的整体顺序,然后建立元素编号与其所在线段树位置的双映射(可以用 hash )。
#include<bits/stdc++.h> using namespace std; const int N=100500000; int n,m,opt,x,y; int ans,L,R,root; int sum[10000000],cnt; int son[10000000][2]; struct node { int tot; int head[1000000]; int nex[1000000]; int rea[1000000]; int val[1000000]; void add(int u,int v) { int has=u%999997; ++tot; nex[tot]=head[has]; head[has]=tot; rea[tot]=u; val[tot]=v; } int ask(int u) { for(int i=head[u%999997];i;i=nex[i]) if(rea[i]==u) return val[i]; return 0; } }pos,rpos; void calc_ask(int &u,int l,int r,int v) { if(!u) u=++cnt; if(l==r) { ++ans; return ; } int mid=(l+r)>>1; int tmp=max(0,min(R,mid)-max(L,l)+1-sum[son[u][0]]); if(v<=mid) calc_ask(son[u][0],l,mid,v); else { ans+=tmp; calc_ask(son[u][1],mid+1,r,v); } } void calc_add(int &u,int l,int r,int v) { if(!u) u=++cnt; ++sum[u]; if(l==r) return ; int mid=(l+r)>>1; if(v<=mid) calc_add(son[u][0],l,mid,v); else calc_add(son[u][1],mid+1,r,v); } void calc_kth(int &u,int l,int r,int v) { if(l==r) { ans=rpos.ask(l); if(!ans) ans=l-m; return ; } int mid=(l+r)>>1; int tmp=max(0,min(R,mid)-max(L,l)+1-sum[son[u][0]]); if(v<=tmp) calc_kth(son[u][0],l,mid,v); else calc_kth(son[u][1],mid+1,r,v-tmp); } int main() { scanf("%d%d",&n,&m); L=m+1;R=n+m; for(int i=1;i<=m;++i) { scanf("%d%d",&opt,&x); if(opt==1) scanf("%d",&y); x-=ans;y-=ans;ans=0; if(opt==1) { int tmp=pos.ask(x); if(!tmp) tmp=x+m; calc_ask(root,1,N,tmp); pos.add(y,tmp); rpos.add(tmp,y); } else if(opt==2) { int tmp=pos.ask(x); if(!tmp) tmp=x+m; calc_ask(root,1,N,tmp); calc_add(root,1,N,tmp); pos.add(x,--L); rpos.add(L,x); } else if(opt==3) { int tmp=pos.ask(x); if(!tmp) tmp=x+m; calc_ask(root,1,N,tmp); calc_add(root,1,N,tmp); pos.add(x,++R); rpos.add(R,x); } else calc_kth(root,1,N,x); printf("%d\n",ans); } return 0; }