Description
有一个容量为V的包,n个接水点,坐标分别为x[]
有两种移动方式:
- 若两个接水点之间的距离不超过此时包的容量v,那么就可以移动
- 若此时v不等于0,那么可以使v=v/2(下取整),然后跳到任意一个位置
对于每一个接水处作为出发点,分别回答能否到达所有其余接水点
n
,
V
≤
1
0
5
n,V\le 10^5
n,V≤105
∀
i
≤
n
,
  
x
i
≤
1
0
9
\forall i\le n,\; x_i\le10^9
∀i≤n,xi≤109
Solution
操作2最多只有logV次,而同一个v能走到的位置是若干不相交的线段
问题变成:我们在V最大的时候强制选一个线段,每一个v最多选一个线段,问能否选出一个线段的集合覆盖整个数轴
考虑状压dp,设f[x]表示选择v状态为x的时候,从1起能覆盖到的最右边,g[x]表示从n起能覆盖到的最左边,当f[x]和g[x]在某一个V的线段内部时就是可行的
一个小trick就是记录left[j]表示j往右最多到哪里,这样转移就非常方便了。最后统计答案的时候要注意一下,一开始写萎T掉了
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define fill(x,t) memset(x,t,sizeof(x))
const int N=400005;
int f[N],g[N],p[N],L[N],R[N],ans[N];
int left[25][N],righ[25][N],bel[N];
int n,V,tot;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void pre(int v,int id) {
for (int i=1,j;i<=n;i=j+1) {
for (j=i;j<n&&p[j+1]-p[j]<=v;) j++;
rep(k,i,j) left[id][i]=j,righ[id][j]=i;
}
}
void solve() {
for (int i=0,lim=(1<<tot);i<lim;++i) {
g[i]=n+1;
}
for (int i=0,lim=(1<<tot);i<lim;++i) {
rep(j,0,tot-1) if ((i>>j)&1) {
f[i]=std:: max(f[i],left[j][f[i-(1<<j)]+1]);
g[i]=std:: min(g[i],righ[j][g[i-(1<<j)]-1]);
}
}
for (int i=0,lim=(1<<tot);i<lim;++i) {
int j=lim-i-1;
if (f[i]+1>=L[bel[f[i]+1]]&&g[j]-1<=R[bel[f[i]+1]]) {
ans[bel[f[i]+1]]=1;
}
}
}
int main(void) {
fill(righ,63);
n=read(),V=read();
rep(i,1,n) p[i]=read();
for (int v=V/2;;v>>=1) {
pre(v,tot++);
if (!v) break;
}
for (int i=1,j;i<=n;i=j+1) {
bel[i]=++bel[0];
for (j=i;j<n&&p[j+1]-p[j]<=V;) bel[++j]=bel[0];
L[bel[0]]=i,R[bel[0]]=j;
}
solve();
rep(i,1,n) if (ans[bel[i]]) puts("Possible");
else puts("Impossible");
return 0;
}