題目鏈接:https://codeforces.com/contest/1288/problem/E
題目大意:
給定一個1到n的排列,現在有m個操作,每個操作給定一個數x,表示將x移動到排列的第一個,其他位置相對不變,問你每個數,他的位置的最大值和最小值分別是多少?
思路:
思路應該是模擬操作,並且用數據結構加速操作,如何操作更快呢?
因爲每次我只移動一個數,其他的數相對位置不會改變,所以考慮將數組擴大一倍。
比如,原數組爲1 2 3 4 5,m={3,5,1,4}
我們將它倒過來(爲什麼倒過來呢?因爲這樣你移動的數都在數組後面了,便於操作),並且在後面加上m個空位,如下
5 4 3 2 1 __ __ __ __
現在將3移動到第一個,也就是數組的最後一個,如下
5 4 __ 2 1 3 __ __ __
這樣的話,3的最小值一定是1(因爲他被操作了,一定會移動到第一位),最大值怎麼算呢?直接用樹狀數組查詢[pos[3],n]這個範圍內有多少個數就可以了。
然後我們更新pos[3],並將之前pos[3]的3刪除掉,這些操作樹狀數組都可以做。
這樣一直模擬m次操作之後,再對每個數更新一遍最大值(和之前一樣,直接查詢最後[pos[3],n]這個範圍內有多少個數就可以了。
問題:
爲什麼不需要每次對每個數最大值的更新呢?比如我做完一個操作,不應該是每個數的最大值都更新了嗎?
回答:
因爲對於這一輪沒有被操作到的數,他的位置一定會往後移動,也就是說對於一個數x,如果他沒有被操作到,他的最大值maxx[x]一定是不斷變大的,所以我們可以等到操作到了x再更新最大值,或者等到最後全體大更新再更新也行。
還有什麼問題也歡迎提出來。
#include <bits/stdc++.h>
using namespace std;
const int maxn=6e5+10;
int pos[maxn];
int c[maxn];
int lowbit(int x){
return x&-x;
}
int n,m;
void add(int x,int v){
while(x<maxn){
c[x]+=v;
x+=lowbit(x);
}
}
int query(int x){
int res=0;
while(x>0){
res+=c[x];
x-=lowbit(x);
}
return res;
}
int maxx[maxn],minn[maxn];
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
pos[i]=i;
maxx[i]=minn[i]=i;
}
reverse(pos+1,pos+n+1);
for(int i=1;i<=n;i++){
add(pos[i],1);
}
for(int i=1;i<=m;i++){
int x;
scanf("%d",&x);
minn[x]=1;
maxx[x]=max(maxx[x],query(maxn-1)-query(pos[x]-1));
add(pos[x],-1);
pos[x]=i+n;
add(pos[x],1);
}
for(int i=1;i<=n;i++){
maxx[i]=max(maxx[i],query(maxn-1)-query(pos[i]-1));
}
for(int i=1;i<=n;i++){
printf("%d %d\n",minn[i],maxx[i]);
}
return 0;
}