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;
}