正题
题目链接:https://www.luogu.com.cn/problem/AT2365
题目大意
一个数轴上有nnn个点,开始你有个水壶容量为VVV,你每次有两个操作
- 走到一个距离与你不超过VVV的点
- 让V=⌊V2⌋V=\lfloor\frac V2\rfloorV=⌊2V⌋,然后跳到任意一个点。
对于每个点求从这个点出发能否走到所有点。
1≤n,V≤2×105,−109≤xi≤1091\leq n,V\leq 2\times 10^5,-10^9\leq x_i\leq 10^91≤n,V≤2×105,−109≤xi≤109
解题思路
显然操作二的次数不会超过logV\log VlogV,考虑从这里入手。考虑有没有一种方案能够把序列分成若干段,每一段相邻的差不超过给其匹配的一个⌊V2x⌋\lfloor\frac{V}{2^x}\rfloor⌊2xV⌋且VVV的段刚好在起点。
并且因为nnn很大我们可以考虑改成用数字而不是下标维护它这一维,设fSf_{S}fS表示用集合SSS中的VVV能够从右边走到的最左边的位置,同理gSg_SgS表示从左边开始走,然后枚举总间段。
注意到这样的复杂度是O(nV)O(nV)O(nV)的,不过如果一段的左右都已经分出了logV\log VlogV个段,那么这个位置也是不合法的(因为)。
时间复杂度:O(VlogV)O(V\log V)O(VlogV)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2e5+10,M=19;
int n,m,V,w[M],x[N],l[M][N],r[M][N],f[1<<M],g[1<<M],ans[N];
int main()
{scanf("%d%d",&n,&V);w[0]=V;while(V){V>>=1;w[++m]=V;}swap(w[0],w[m]);for(int i=1;i<=n;i++)scanf("%d",&x[i]);for(int j=0;j<=m;j++){l[j][1]=1;r[j][n]=n;for(int i=2;i<=n;i++)l[j][i]=(x[i]-x[i-1]<=w[j])?l[j][i-1]:i;for(int i=n-1;i>=1;i--)r[j][i]=(x[i+1]-x[i]<=w[j])?r[j][i+1]:i;}int MS=(1<<m);for(int s=0;s<MS;s++){f[s]=n+1;for(int i=0;i<m;i++){if(!((s>>i)&1))continue;f[s]=min(f[s],l[i][f[s^(1<<i)]-1]);g[s]=max(g[s],r[i][g[s^(1<<i)]+1]);}}int cnt=0;for(int i=1;i<=n;i++){bool flag=0;for(int s=0;s<MS;s++){int t=(MS-1)^s;if(g[s]>=i-1&&f[t]<=r[m][i]+1){flag=1;break;}}for(int j=i;j<=r[m][i];j++)ans[j]|=flag;cnt++;i=r[m][i];if(cnt>m)break;}cnt=0;for(int i=n;i>=1;i--){bool flag=0;for(int s=0;s<MS;s++){int t=(MS-1)^s;if(f[s]<=i+1&&g[t]>=l[m][i]-1){flag=1;break;}}for(int j=i;j>=l[m][i];j--)ans[j]|=flag;cnt++;i=l[m][i];if(cnt>m)break;}for(int i=1;i<=n;i++)if(ans[i])puts("Possible");else puts("Impossible");return 0;
}