Codeforces Round #751 (Div. 2)-D. Frog Traveler
传送门
题意:
一只青蛙在位置 \(n\),在位置 \(i\) 每次可以向上跳 \(1\) ~ \(a[i]\) 格,每次跳到位置 \(j\) 会下滑 \(b[j]\) 格,问最少的跳跃次数跳到井口。输出方案。
做法:
设 \(f[i]\) 表示位置 \(i\) 先下滑后跳到顶部(位置 \(0\)) 所需的最小步数,利用线段树记录区间 \(f[i]\) 的最小值与最小值位置,求 \(f[i]\) 取其下滑后位置可以上跳到的位置中最小步数加一即可,不用担心下面会有一些小于 \(i\) 的地方 \(f\) 未更新,因为是从下向上跳,并且可跳的步数为 \(1\) ~ \(a[i]\),因此既然能跳到 \(i\),那必然能跳到 \(i\) 滑下再跳的地方,跳到 \(i\) 滑下去再跳显然不优。
code:
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+8;
int n;
struct node{
int x,t;
node(int xx=1061109568,int tt=-1){x=xx;t=tt;}
} tr[N<<2];
int a[N],b[N];
int ans[N];
void ch(int k,int l,int r,int x,int v)
{
if(l==r){
tr[k].x=v;
tr[k].t=l;
return;
}
int mid=l+r>>1;
if(x<=mid) ch(k<<1,l,mid,x,v);
else ch(k<<1|1,mid+1,r,x,v);
tr[k]=tr[k<<1].x<tr[k<<1|1].x?tr[k<<1]:tr[k<<1|1];
}
node ask(int k,int l,int r,int x,int y)
{
if(x>y) return node{1061109568,-1};
if(x<=l&&r<=y) return tr[k];
int mid=l+r>>1;
node t1,t2;
if(x<=mid) t1=ask(k<<1,l,mid,x,y);
if(y>mid) t2=ask(k<<1|1,mid+1,r,x,y);
return t1.x<=t2.x?t1:t2;
}
int main()
{
memset(tr,0x3f,sizeof(tr));
cin>>n;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]);
for(int i=1;i<=n;i++)
{
if(a[i+b[i]]>=i+b[i]) ch(1,1,n,i,1);
else{
node tt=ask(1,1,n,max(1,min(i+b[i],n)-a[min(i+b[i],n)]),min(i+b[i],n));
if(tt.x!=-1) ch(1,1,n,i,tt.x+1),ans[i]=tt.t;
}
}
node pp=ask(1,1,n,n,n);
if(pp.x>=1061109568) puts("-1");
else {
printf("%d\n",pp.x);
int xx=n;
while(xx>0){
xx=ans[xx];
printf("%d ",xx);
}
}
}