1. 程式人生 > >AGC 012 E Camel and Oases - 狀壓dp

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
題解:
首先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;
}