洛谷【P3407】散步

我对状态空间的理解:https://www.cnblogs.com/AKMer/p/9622590.html

题目传送门:https://www.luogu.org/problemnew/show/P3407

虽然这道题值域范围很大,但是(n)还在可以接受的范围。

我们灵性的理解一下,如果一个人运气不好,碰不到其他人,他将一直走下去。所以我们就可以在(O(1))的时间内算出他最后的位置。

我们再灵性的想一下,如果一个人碰到了人群,那么他就将停止走路,在人群中一起聊天。

人群个数不会超过(n/2),这很显然对吧。我们只要掌握每一个会形成人群的点,就可以算出所有人最后的位置了。

然后某两个人可以碰到一起当且仅当(i)号人向东走且(i+1)号人向西走,(pos[i+1]-pos[i]leqslant2*T),那样就会在(pos[i]+(pos[i+1]-pos[i])/2)的地方形成人群。这时我们再去更新上一个人群这一个人群向东走的人的答案,因为新人群的出现只会影响这满足上述条件的人。然后向西走的就直接算会不会走到上一个人群里,不会的话就直接走,会的话上一个人群的位置就是他最后的位置。

时间复杂度:(O(n))

空间复杂度:(O(n))

代码如下:

#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long

const int maxn=1e5+5;

int di[maxn];//记录每个人的方向
int n,Q,now=1;//now是上一个人群后的第一个人的标号
ll T,lst=-3e18;//T记录时间,lst记录上一个人群的位置
ll pos[maxn],POS[maxn];//pos记录初始位置,POS记录最后位置

int main() {
    scanf("%d%lld%d",&n,&T,&Q);
    for(int i=1;i<=n;i++)
        scanf("%lld%d",&pos[i],&di[i]);//读入
    for(int i=1;i<=n;i++) {
        if(di[i]==2) POS[i]=max(pos[i]-T,lst);//如果是向西走那么直接对pos[i]-T,lst取max
        else {
            POS[i]=pos[i]+T;//我不管最终位置是啥,先放到pos[i]+T再说。到时候形成新人群了会回过头来更新
            if(di[i+1]==2&&pos[i+1]-pos[i]<=2*T) {//如果会形成新人群
                lst=pos[i]+(pos[i+1]-pos[i])/2;//更新人群位置
                while(now<i+2) {
                    if(di[now]==1)POS[now]=min(pos[now]+T,lst);
                    now++;//把上一个人群到当前人群间所有向东走的人的答案都试着更新一遍。
                }
            }
        }
    }
    for(int i=1;i<=Q;i++) {
        int x;scanf("%d",&x);
        printf("%lld
",POS[x]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/AKMer/p/9637680.html