状压就状压,考试时干嘛分层……
通过状压枚举前后缀就可以枚举所有情况了。
预处理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。
在预处理时不考虑扔掉不跳跃的行动力,最后手动处理一下。
还有记得没有行动力也可以通过跳转来占据一个点,所以要多加一个行动力为0的段。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
const int N=2e5+2;
int n,bit,vl[19],dp[N],pos[N],dpl[1<<18],dpr[1<<18],to_l[N][19],to_r[N][19];
//dp[i]: 左侧覆盖1~i,右侧最多能够覆盖dp[i]~n(不使用未跳跃)
template <class T> inline void apn(T &x,const T y){
if(x>y) x=y;
}
template <class T> inline void apx(T &x,const T y){
if(x<y) x=y;
}
inline int nxi(){
int x=0;
char c;
while(((c=getchar())>'9'||c<'0')&&c!='-');
const bool f=c=='-'&&(c=getchar());
while(x=x*10-48+c,(c=getchar())>='0'&&c<='9');
return f?-x:x;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("d.in","r",stdin);
#endif
memset(dp,1,sizeof(dp));
memset(dpr,1,sizeof(dpr));
n=nxi(),vl[0]=nxi();
for(;vl[bit];++bit){
vl[bit+1]=vl[bit]>>1;
}
for(int i=1;i<=n;++i){
pos[i]=nxi();
}
for(int i=0;i<=bit;++i){
for(int s=1,t;s<=n;s=t+1){
t=s;
while(t<n&&pos[t+1]-pos[t]<=vl[i]) ++t;
for(int j=s;j<=t;++j){
to_l[j][i]=s,to_r[j][i]=t;
}
}
}
dpl[0]=0,dpr[0]=n+1;
for(int i=0;i<1<<bit;++i){
for(int j=1;j<=bit;++j){
if(!(i&(1<<(j-1)))){
apx(dpl[i|1<<(j-1)],to_r[dpl[i]+1][j]);
if(dpr[i]){
apn(dpr[i|1<<(j-1)],to_l[dpr[i]-1][j]);
}
else dpr[i|1<<(j-1)]=0;
}
}
}
for(int i=0;i<1<<bit;++i){
apn(dp[dpl[i]],dpr[((1<<bit)-1)^i]);
}
for(int i=n;i;--i){
apn(dp[i-1],dp[i]);
}
for(int i=1;i<=n;++i){
puts(dp[to_l[i][0]-1]<=to_r[i][0]+1?"Possible":"Impossible");
}
return 0;
}