题目大意:一排点,两点间有距离。 初始你有一个行走值$v$,如果相邻两点距离不超过$v$你可以自由在这两点行走。
当$v$大于$0$时,你可以选择某一时刻突然飞到任意点,这样做后$v$会减半(下取整)。 问从每个位置初始出发能否到达所有位置。
点的数量$≤2*10^5$,$v≤2*10^5$,$|两点距离|≤10^9$。
我们令$l[i][j]$表示从$i$出发,一路往左走,经过所有长度不超过$v>>j$(此处的$>>$表示右移,以下都是)的边,能走到最左的点的编号。
令$r[i][j]$表示从$i$出发,一路往右走,经过所有长度不超过$v>>j$的边,能走到最右的点的编号。
令$n$表示点的数量,$m=lceil log_2v ceil$。
我们不难得出:从$u$号点出发,是否可以遍历完所有点的判断条件,可以转化为:
是否可以将点集分成$m+1$个块,且第$i$(从$0$到$m$)个块内边的长度均不超过$v>>i$,且第$u$号点需要在第$0$个块内。
那么,对于$[1,2^m)$中的每一个$i$($i$是一个二进制状态,$i$的第$j$($j$从$1$到$m$)位为$1$表示选择了图中第$j$个块)
求一个最大的$f[i]$,满足区间$[1,f[i]]$中的点能分成由状态i表示的若干个块。
同理,求一个最小的$g[i]$,满足区间$[g[i],n]$中的点能分成由状态i表示的若干个块。
求这个可以通过l和r的值+状压$dp$实现,时间复杂度是$O(v log v)$。
我们令$o=2^m-2$。
我们发现,若存在$i$,使得$r[f[i]][0]>=l[g[o$^$i]][0]$,那么从区间$[ l[g[o$^$i]][0] , r[f[i]][0] ]$中出发的点,显然可以遍历玩所有点。
我们可以$O(1)$打上一个标记,求答案的时候$O(n)$扫一遍,判断某个点是否被打了标记即可。
总时间复杂度:$O(n log v+v log v)$。
1 #include<bits/stdc++.h> 2 #define M 400005 3 #define YXQAK printf("Possible ") 4 #define XFZBL printf("Impossible "); 5 using namespace std; 6 7 int a[M]={0},n,m,v,l[20][M]={0},r[20][M]={0}; 8 int f[M]={0},g[M]={0},p[M]={0}; 9 10 int main(){ 11 scanf("%d%d",&n,&v); 12 for(int i=1;i<=n;i++) scanf("%d",a+i); 13 sort(a+1,a+n+1); 14 for(int j=0,V=v;V;j++,V>>=1){ 15 m=max(m,j); 16 for(int i=1;i<=n;i++){ 17 int I=i+1; 18 while(I<=n&&a[I]-a[I-1]<=V) I++; 19 I--; 20 for(int ii=i;ii<=I;ii++) 21 l[j][ii]=i,r[j][ii]=I; 22 i=I; 23 } 24 } 25 m++; 26 for(int i=1;i<=n;i++) l[m][i]=r[m][i]=i; 27 for(int i=0;i<(1<<m);i++) g[i]=n; f[0]=1; 28 for(int i=1;i<(1<<m);i++){ 29 int now=1; 30 for(int j=m-1;~j;j--) 31 if((1<<j)&i) 32 f[i]=max(f[i],r[j+1][f[i^(1<<j)]]+1); 33 34 now=n; 35 for(int j=m-1;~j;j--) 36 if((1<<j)&i) 37 g[i]=min(g[i],l[j+1][g[i^(1<<j)]]-1); 38 } 39 40 for(int i=0;i<(1<<m);i++){ 41 if(r[0][f[i]]+1>=l[0][g[(1<<m)-i-1]]) 42 p[l[0][g[(1<<m)-i-1]]]++,p[r[0][f[i]]+1]--; 43 } 44 for(int i=1;i<=n;i++){ 45 p[i]+=p[i-1]; 46 if(p[i]) YXQAK; 47 else XFZBL; 48 } 49 }