Codeforce 1288 E. Messenger Simulator (思维+树状数组)

题目链接: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;

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章