链接
因为 (n leq 10^8),(m leq 10^5),所以可以用一个域很大的权值线段树动态开点维护。
开始 (L=1),(R=n),([L,R]) 的点值看作 (1),把一个位置的数提前相当于 (L-1) 的位置加 (1),原来的位置 (1) 变为 (0),放后也类似。
用两个 (map) 维护数的位置和编号的相互映射。
#include<bits/stdc++.h>
#define IL inline
#define LL long long
using namespace std;
const int N=1e5+3,inf=1e8+1e5+10;
int n,m,ans,L,R,rt;
map<int,int>mp1,mp2;
IL int in(){
char c;int f=1;
while((c=getchar())<'0'||c>'9')
if(c=='-') f=-1;
int x=c-'0';
while((c=getchar())>='0'&&c<='9')
x=x*10+c-'0';
return x*f;
}
struct hh{
int cnt,ls[N*400],rs[N*400],sum[N*400];
void upd(int &o,int l,int r,int u){
if(!o) o=++cnt;++sum[o];
if(l==r) return;
int mid=l+r>>1;
if(u<=mid) upd(ls[o],l,mid,u);
else upd(rs[o],mid+1,r,u);
}
int ask(int o,int l,int r,int rr){
if(!o) return 0;
if(r<=rr) return sum[o];
int mid=l+r>>1;
if(rr<=mid) return ask(ls[o],l,mid,rr);
return ask(ls[o],l,mid,rr)+ask(rs[o],mid+1,r,rr);
}
int kth(int o,int l,int r,int k){
if(l==r) return l;
int mid=l+r>>1,res=max(0,min(R,mid)-max(L,l)+1)-sum[ls[o]];
if(res>=k) return kth(ls[o],l,mid,k);
return kth(rs[o],mid+1,r,k-res);
}
}T;
int query(int x){
if(mp1.find(x)!=mp1.end()) x=mp1[x];
return x-L-T.ask(rt,-inf,inf,x-1)+1;
}
void work1(int x,int y){
if(mp1.find(x)!=mp1.end()) mp2[mp1[x]]=y,mp1[y]=mp1[x];
else mp2[x]=y,mp1[y]=x;
printf("%d
",ans=query(y));
}
void work2(int x){
printf("%d
",ans=query(x));int y=x;
if(mp1.find(x)!=mp1.end()) x=mp1[x];
T.upd(rt,-inf,inf,x),mp1[y]=--L,mp2[L]=y;
}
void work3(int x){
printf("%d
",ans=query(x));int y=x;
if(mp1.find(x)!=mp1.end()) x=mp1[x];
T.upd(rt,-inf,inf,x),mp1[y]=++R,mp2[R]=y;
}
void work4(int k){
int x=T.kth(rt,-inf,inf,k);
if(mp2.find(x)!=mp2.end()) printf("%d
",ans=mp2[x]);
else printf("%d
",ans=x);
}
int main()
{
int op,x,y;
n=in(),m=in(),L=1,R=n;
for(int i=1;i<=m;++i){
op=in(),x=in()-ans;
if(op==1) work1(x,in()-ans);
else if(op==2) work2(x);
else if(op==3) work3(x);
else work4(x);
}
return 0;
}