如果解决区间不重复的数的个数----只维护某个数最后一次出现的位置!!!---E. Messenger Simulator

E. Messenger Simulator
题意:一开始有一个1到n的数组,然后m个操作,操作输入ai,将ai移动到数组的第一个位置,求在m个操作的过程中每个数的最小位置和最大位置。
思路:首先maxl[i],maxr[i]代表每个数的最小和最大位置,进行初始化,然后遍历一遍操作a数组,然后当第一次碰到一个数时,这个数的maxl即为1,然后maxr即为之前出现过的数中小于这个数的个数+这个数初始的位置(不重复计数),如果当第二次遇到某个数问题就转化成在第一次到第二次这段时间里有几个不同的数当了队首,这个一开始想用主席树,可是发现根本没法处理,然后忽然想到了每个相同的数有意义的就是那个数最后出现的那次,就像之前做线段树的题,不重复计数的话r边界依次增加则只有某个数最后一次出现时才有意义,这样就可以数状数组维护位置了,如果某个数第二次出现,则第二次的位置加1,第一次出现的位置减1,这样就可以解决这个问题了,注意的是维护位置的树状数组add操作应该while(p<=m),因为位置是1-m。最后的时候再处理一遍所有的值即可。

#include<iostream>
#include<cstdio>
using namespace std;
const int MAX_N=301000;
int a[MAX_N],maxl[MAX_N],maxr[MAX_N];
int vis[MAX_N];
int sum[MAX_N],n,m,sum1[MAX_N];
void add(int p,int x){
 while(p<=n){
  sum[p]+=x;
  p+=p&-p;
 }
}
int ask(int p){
 int ans=0;
 while(p){
  ans+=sum[p];
  p-=p&-p;
 }
 return ans;
}
void add1(int p,int x){
 while(p<=m){
  sum1[p]+=x;
  p+=p&-p;
 }
}
int ask1(int p){
 int ans=0;
 while(p){
  ans+=sum1[p];
  p-=p&-p;
 }
 return ans;
}
int main(void){
 int i,j;
 scanf("%d%d",&n,&m);
 for(i=1;i<=n;i++){
  maxl[i]=i;
  maxr[i]=i;
 }
 for(i=1;i<=m;i++){
  scanf("%d",&a[i]);
  if(!vis[a[i]]){
   vis[a[i]]=i;
   maxl[a[i]]=1;
   maxr[a[i]]=a[i]+ask(n)-ask(a[i]);
   add(a[i],1);
   add1(i,1);
  }
  else{
   maxr[a[i]]=max(maxr[a[i]],ask1(i)-ask1(vis[a[i]])+1);
   add1(vis[a[i]],-1);
   add1(i,1);
   vis[a[i]]=i;
  }
 }
 for(i=1;i<=n;i++){
  if(!vis[i]){
   maxr[i]=i+ask(n)-ask(i);
  }
  else{
   maxr[i]=max(maxr[i],ask1(m)-ask1(vis[i])+1);
  }
  //cout<<vis[i]<<" vis\n";
  printf("%d %d\n",maxl[i],maxr[i]);
 }
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值