一本通OJ-队伍整理
队伍整理
题意
给定一个队列里的一些人的排名数列,而后我们进行两个操作。
- 询问排在\(i\)位置的人前面的人最好的成绩是多少。
- 排在\(i\)位置的人移动到队尾。
最后我们需要输出最少移动多少同学使得队伍没有空隙。
分析
我们考虑使用线段树维护队列位置上的人的排名。
对于\(1\)操作我们可以求位置\(i\)前的线段树上的\(Min\)排名,对于\(2\)操作我们可以将原位置排名赋值为\(inf\)而后我们讲该排名移动到线段树上\(len+1\)的位置。对于询问我们可以将有人的位置打上标记,我们维护\(vis\)数组对应的前缀和数组\(sum[i]\)。而后我们枚举求出\(sum[i]-sum[i-n]\)的最大值。结果就是\(n-Max(sum[i]-sum[i-n])\)。
我们注意需要注意线段树的空间是\(n+m\)而我们查询的时候同理查询上限是\(n+m\)而不是\(n\)。
代码
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define ls (k<<1)
#define rs (k<<1|1)
const int N=2e5+5;
const int inf=1e9+9;
int n,m,a[N],sum[N],b[10000010],tr[N<<2];
int vis[N];
struct seg_tree{
void pushup(int k,int l,int r){
tr[k]=min(tr[ls],tr[rs]);
}
void build(int k,int l,int r){
if(l==r) return tr[k]=a[l],void();
build(ls,l,mid),build(rs,mid+1,r);
pushup(k,l,r);
}
void modify(int k,int l,int r,int x,int val){
if(l==r) return tr[k]=val,void();
if(x<=mid) modify(ls,l,mid,x,val);
else modify(rs,mid+1,r,x,val);
pushup(k,l,r);
}
int query(int k,int l,int r,int x,int y){
if(y<x) return inf;
if(x<=l&&r<=y) return tr[k];
int res=inf;
if(x<=mid) res=min(res,query(ls,l,mid,x,y));
if(y>=mid+1) res=min(res,query(rs,mid+1,r,x,y));
return res;
}
}T;
char op;
int main(){
scanf("%d%d",&n,&m);
memset(a,0x3f,sizeof a);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=i;
T.build(1,1,n+m);
int len=n;
for(int i=1;i<=m;i++){
cin>>op;
int x;scanf("%d",&x);
if(op=='A'){
if(!b[x]){printf("-1\n");continue;}
int ans=T.query(1,1,n+m,1,b[x]-1);
if(ans==inf) ans=-1;
printf("%d\n",ans);
}
else{
T.modify(1,1,n+m,b[x],inf);
b[x]=++len;
T.modify(1,1,n+m,b[x],x);
}
}
int res=0;
for(int i=1;i<=n;i++) vis[b[a[i]]]=1;
for(int i=1;i<=n+m;i++) sum[i]=sum[i-1]+vis[i];
for(int i=n;i<=n+m;i++) res=max(res,sum[i]-sum[i-n]);
res=n-res;
printf("%d",res);
return 0;
}