题意:
井下深度为n,在井下深度为i的地方可以往上跳[0,ai]米
如果没有跳出井,则要下滑bi米
问最少跳几次可以跳出井
假设跳[1,d]可以出井的位置都求出来了
现在求跳d+1次可以出井的位置
枚举跳d次可以出井的位置v,若从位置u可以滑倒位置v,即u+b[u]=v,
那么对于一个没有算过的位置j,如果他能跳到位置u,那么从位置j就是跳d+1次出井
即 j-a[j]<=u<=j
可以采用队列以此计算d=1,2.3……
每次取队首作为v
u可以提前预处理
求位置j可以用线段树,把j-a[j]放到第j个叶子节点,求区间最小值
#include<bits/stdc++.h> using namespace std; #define N 300004 int a[N],b[N],f[N]; int tr[N<<2],w[N<<2]; queue<int>q; vector<int>v[N]; int pos,mi; int front[N],to[N]; void build(int k,int l,int r) { if(l==r) { tr[k]=l-a[l]; w[k]=l; return; } int mid=l+r>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); if(tr[k<<1]<=tr[k<<1|1]) { tr[k]=tr[k<<1]; w[k]=w[k<<1]; } else { tr[k]=tr[k<<1|1]; w[k]=w[k<<1|1]; } } void query(int k,int l,int r,int opl,int opr) { if(l>=opl && r<=opr) { if(tr[k]<mi) { mi=tr[k]; pos=w[k]; } return; } int mid=l+r>>1; if(opl<=mid) query(k<<1,l,mid,opl,opr); if(opr>mid) query(k<<1|1,mid+1,r,opl,opr); } void change(int k,int l,int r,int p) { if(l==r) { tr[k]=1e9; return; } int mid=l+r>>1; if(p<=mid) change(k<<1,l,mid,p); else change(k<<1|1,mid+1,r,p); if(tr[k<<1]<=tr[k<<1|1]) { tr[k]=tr[k<<1]; w[k]=w[k<<1]; } else { tr[k]=tr[k<<1|1]; w[k]=w[k<<1|1]; } } int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i) { scanf("%d",&b[i]); v[i+b[i]].push_back(i); } v[0].push_back(0); build(1,0,n); f[0]=0; q.push(0); change(1,0,n,0); int now,st; while(!q.empty()) { now=q.front(); q.pop(); for(int i=0;i<v[now].size();++i) { st=v[now][i]; while(1) { pos=-1; mi=1e9; query(1,0,n,st,n); if(mi>v[now][i]) break; f[pos]=f[now]+1; q.push(pos); change(1,0,n,pos); front[pos]=now; to[pos]=st; } } } if(!f[n]) { printf("-1"); return 0; } printf("%d\n",f[n]); now=n; while(now) { printf("%d ",to[now]); if(!to[now]) break; now=front[now]; } }