Codeforces Round #751 (Div. 2) D. Frog Traveler(优先队列)

链接

题意:

输入n代表n米深的井。接着一行输入n个数a_{i}代表在深度为i的位置能往上跳0a_{i}个单位,再输入n个数b_{i}代表在i位置会往下滑b_{i}个单位。每次上跳都会紧随一次下滑(跳出井除外),问至少要多少次上跳才能跳出井。输出步数和每次跳完到达的位置。

n<=3e5,0<=a_{i}<=i,0<=b_{i}<=n-i

思路:

上网看别人bfs的题解看得头大,所以决定自己写一篇。

蒟蒻上路请多包涵QwQ

第一眼看到这题想到了dp,于是试着列了个递推式。定义dp[i]为到达深度为i的位置的最少步数。

dp[i]=Min\left \{dp[j]+1 \right \},i<=j-b[j]+a[j-b[j]]

想着如何优化的时候发现了一个思路:就一个位置i来说,能到达的位置的最小值是能唯一确定的。那就好办了,假设从j能到达的最高点为next_{j},到达j的最少步数为step_i,那么对于某个位置i来说,我们肯定去找满足next_j小于i且step_j最小的点去更新。这可以用类似二维偏序的思路解决:树状数组维护step_i,每更新到一个位置i,先查询0到i的区间最小值来更新当前的step,再用step_i更新树状数组以next_i为下标的最小值。

码完之后仔细一看样例,要输出路径,但上面的思路显然不能维护路径。真是绝绝子,所以恭喜你看了我写的那么多废话。

虽然树状数组不行,但是同样的思路我们可以用优先队列实现。树状数组找最小的step的思路可以用优先队列的排序实现,更新的话就用优先队列自带的pushpop。详细思路可以结合代码思考。因为要维护路径,所以维护一个pre记录前继,最后递归输出就行。复杂度O(nlgn),应该不是最快的办法,但是很好理解,也能过题,耗时171 ms。

代码:

#include<bits/stdc++.h>
using namespace std;
const int M = 3e5+10;
#define IO ios::sync_with_stdio(false);cin.tie(0)
#define INF 0x3f3f3f3f
#define ll long long
int n;
int a[M],b[M],pre[M];
struct node{
    int to,from,step;
    bool operator<(const node& b)const{
        return step>b.step;
    }
};
void print(int x){
    if(x==n)return;
    print(pre[x]);
    cout<<x<<" ";
}
int main(){
#ifdef DEBUG
    freopen("in.txt","r",stdin);
#else
    IO;
#endif
    cin>>n;
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n;++i)cin>>b[i];
    priority_queue<node> pq;
    pq.push({n-a[n],n,0});
    int ans=INF;
    for(int i=n-1;i>=0;--i){
        while(!pq.empty()&&pq.top().to>i)pq.pop();
        if(pq.empty())break;
        pre[i]=pq.top().from;
        if(!i)ans=pq.top().step+1;
        pq.push({i+b[i]-a[i+b[i]],i,pq.top().step+1});
    }
    if(ans==INF)cout<<-1<<endl;
    else{
        cout<<ans<<endl;
        print(0);
        cout<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值