一本通OJ-队伍整理

队伍整理

题意

给定一个队列里的一些人的排名数列,而后我们进行两个操作。

  1. 询问排在\(i\)位置的人前面的人最好的成绩是多少。
  2. 排在\(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;
}
posted @ 2023-07-25 14:08  Zimo_666  阅读(14)  评论(0编辑  收藏  举报