AGC 012 E Camel and Oases - 状压dp

题目大意:数轴上有n个不同的点,你有一个能量,初始是v。可以进行两种操作,走到左边/右边一个点,如果当前能量大于等于距离。或者随意跳到一个点,但是要求能量不是0并且能量要减半(向下取整)。对每个点求从这个点出发能否到达所有点。一个点可以经过多次。 n , v ≤ 2 × 1 0 5 , ∣ x i ∣ ≤ 1 0 9 n,v\le2\times10^5,|x_i|\le10^9 n,v2×105,xi109
题解:
首先v只会减半logv次就拜拜了。
考虑一开始在一个点,你能到达一个区间。
然后你需要把哪些减半后的v用于走左边,剩下的走右边。
直接枚举子集就死了,转为求如果想要从1走到i,最小能从多小的j出发走到n。处理LF[S]表示S这个集合的v最远能从1走到那里,RF同理,然后就做完了。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
using namespace std;inline int inn() { int x;scanf("%d",&x);return x; }const int N=270000,LOG=20;int x[N],v[N],L[LOG][N],R[LOG][N],val[N],Lf[N],Rf[N];
int main()
{
    int n=inn(),k=0;v[0]=inn();rep(i,1,n) x[i]=inn();while(v[k]) v[k+1]=v[k]>>1,k++;int all=(1<<k)-1;
    rep(i,0,k)
    {
        L[i][1]=1;for(int j=1+1;j<=n;j++) if(x[j]-x[j-1]<=v[i]) L[i][j]=L[i][j-1];else L[i][j]=j;
        R[i][n]=n;for(int j=n-1;j>=0;j--) if(x[j+1]-x[j]<=v[i]) R[i][j]=R[i][j+1];else R[i][j]=j;
    }
    rep(i,1,all) Lf[i]=1,Rf[i]=n;Lf[0]=0,Rf[0]=n+1;
    rep(i,0,all) rep(j,1,k) if(!((i>>(j-1))&1)) Lf[i|(1<<(j-1))]=max(Lf[i|(1<<(j-1))],R[j][Lf[i]+1]),Rf[i|(1<<(j-1))]=min(Rf[i|(1<<(j-1))],L[j][Rf[i]-1]);
    rep(i,0,n+1) val[i]=n+n;rep(i,0,all) val[Lf[i]]=min(val[Lf[i]],Rf[all^i]);for(int i=n;i>=0;i--) val[i]=min(val[i],val[i+1]);
    rep(i,1,n) printf(val[L[0][i]-1]<=R[0][i]+1?"Possible\n":"Impossible\n");return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值