[agc012e]Camel and Oases

前言

很容易的就发现了只有log次跳跃。
然后状压DP。
似乎就是个简单题吧(怎么比12c还简单)

题意

一排点,两点间有距离。
初始你有一个行走值v,如果相邻两点距离不超过v你可以自由在这两点行走。
当v大于0时,你可以选择某一时刻突然飞到任意点,这样做后v会减半(下取整)。
问从每个位置初始出发能否到达所有位置。

DP

预处理left[i,j]表示在i行走值已经减半j次能往左走到哪,同理有right。
我们每次从i点出发,用行走值v可以走到[l,r]。
那么你需要将接下来的行走值分成两部分然后覆盖[1,l-1]和[r+1,n]。
我们希望预处理一个mi[i]表示用行走值中的一部分覆盖[1,i],另一部分能覆盖[x,n],x的最小值是多少。
然后为了这个先状压DP一下,设f[s]表示用s集合里的行走值覆盖[1,x]最大的x是多少,每次可以枚举一个不在s里的i然后转移,用这个i去覆盖接下来一段。
同理会有个g[s]表示覆盖[x,n]。
于是接下来再枚举拿哪些覆盖前缀即可处理mi。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=200000+10,lgn=20;
int f[maxn*5],g[maxn*5],mi[maxn],left[maxn][lgn+10],right[maxn][lgn+10],v[lgn+10],x[maxn];
int i,j,k,l,r,s,t,n,m,tot,top;
int main(){
    scanf("%d%d",&n,&t);
    fo(i,1,n) scanf("%d",&x[i]);
    v[0]=t;
    t/=2;
    while (t){
        v[++top]=t;
        t/=2;
    }
    v[++top]=0;
    reverse(v,v+top+1);
    fo(j,0,top){
        left[1][j]=1;
        fo(i,2,n)
            if (x[i]-x[i-1]<=v[j]) left[i][j]=left[i-1][j];else left[i][j]=i;
        right[n][j]=n;
        fd(i,n-1,1)
            if (x[i+1]-x[i]<=v[j]) right[i][j]=right[i+1][j];else right[i][j]=i;
    }
    f[0]=0;
    g[0]=n+1;
    fo(s,1,(1<<(top+1))-1){
        f[s]=0;
        g[s]=n+1;
        fo(i,0,top)
            if (s&(1<<i)){
                r=s^(1<<i);
                if (f[r]==n) f[s]=n;
                else f[s]=max(f[s],right[f[r]+1][i]);
                if (g[r]==1) g[s]=1;
                else g[s]=min(g[s],left[g[r]-1][i]);
            }
    }
    fo(i,0,n) mi[i]=n+2;
    fo(i,0,(1<<top)-1){
        r=((1<<top)-1)^i;
        mi[f[i]]=min(mi[f[i]],g[r]);
    }
    fd(i,n-1,0) mi[i]=min(mi[i],mi[i+1]);
    fo(i,1,n){
        l=left[i][top];r=right[i][top];
        if (mi[l-1]<=r+1) printf("Possible\n");else printf("Impossible\n");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值