题目大意
坐标轴上有$n$个坐标,第$i$个坐标是$x_i$,初始你有一个容量$V$,当两个给定的坐标距离不超过$V$时,你可以从一个坐标到达另一个坐标,同时你还可以令$V=\lfloor \frac{V}{2}\rfloor$,并到达一个任意一个给定的坐标。
求对于每一个点是否存在一种方案使得从这个点出法能够到达每一个点至少一次。
题解
首先$V$的值只有$\log V$种,对于每一种取值,会有若干段下标连续的坐标可以互相到达,要求在每一层取一段使得所有坐标都被覆盖,题意即为在强制选第一层的某一段,是否存在合法方案。
考虑先预处理每一层每一个坐标能到达的最左和最右的坐标,再在忽略第一层的意义下将每一层是否被选中的状态压缩起来,求出用了某些层所能覆盖的最长前缀和后缀。
设某一选中的状态集合为$K$,全集为$S$,$K$所能覆盖的最长前缀为$Pre_K$,最长后缀为$Suf_K$。
枚举$K$,对于每一个$Pre_K$,求出它所对应的最长的$Suf$即为$F_{Pre_K}$,这个用$Suf_{S-K}$更新即可,要考虑$F_i+1$可以更新$F_i$。
所以最后枚举每一个坐标$i$,找到它第一层所在的段的左右端点$L,R$,看$F_{L-1}$是否能覆盖到$R$即可。
由于状态数是$2^{\log_2 V}$,所以它是严格小于$V$的,所以最终复杂度为$O(n\log V+V)$。
#include#include #include #include #include #define M 200020#define INF 1000001000using namespace std;const int BS=1<<19;char BF[BS],OT[BS],*SZ=OT,*HD,*TL;const char *ED=OT+BS-1; int Top;char Getchar(){if(HD==TL)TL=(HD=BF)+fread(BF,1,BS,stdin);return *HD++;}void flush(){fwrite(OT,1,SZ-OT,stdout);}void Putc(char c){*SZ++ =c;if(SZ==ED) flush(),SZ=OT;}int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh;}void TK(bool x){ if(x) Putc('P'); else Putc('I'),Putc('m'),Putc('p'); Putc('o'); Putc('s'),Putc('s'),Putc('i'),Putc('b'),Putc('l'),Putc('e'),Putc('\n');}int tot,n,m,pos[M],L[M][21],R[M][21],V[40],rt;int RF[M<<3],LF[M<<3],G[M],D[M],MAXN;int main(){ n=read(),m=read(); for(int i=1;i<=n;i++) pos[i]=read(),D[i]=pos[i]-pos[i-1],G[i]=-(INF<<1); while(m) V[tot++]=m,m>>=1; tot++; for(int k=0;k 0&&D[i-L[i][k]+1]<=V[k]) L[i][k]++; } } MAXN=(1< =0;i--) G[i]=max(G[i],G[i+1]); for(int i=1;i<=n;i++) TK(G[i-L[i][0]]+R[i][0]+i-1>=n); flush();return 0;}