题目笔记——AT_abc146_f [ABC146F] Sugoroku

其实也可以不用单调队列就做出来了。

思路

本题中要维护两个最小值:回合最小和字典序最小。

  • 先看回合最小,我们只需要在每次前进时走到能往前走的最远下标,就可以求出回合最小。
  • 但这样的话所得到的字典序就会最大,因为只要能从起点走到终点,就可以从终点原路返回到起点,而且回合数一样,于是想到从后往前遍历,也是每次前进时走到能往前走的最远下标,这样既维护了回合最小值,又保证了字典序最小。

1. 预处理

先从后往前预处理出每个点及它以后第一个不为“GameOver 格”的下标,以便查询,复杂度为 \(O(n)\)。

2. 遍历

判断如果能走到的所有地方都为“GameOver 格”,则说明 \(1\) 到 \(n\) 之间走不通,否则把 \(i\) 更新为往前走所能走到的最远下标。

3. 输出

由于是倒序遍历,所以可以用栈暂时存上答案的下标,结束后在输出下标的差就好了。

本蒟蒻的第一篇题解,大佬勿喷。

Code:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 20;
int n, m, cnt, ans, num[N];
string s;
stack<int> stk;

signed main(){
    cin >> n >> m >> s;
    num[n] = n;
    for(int i = n - 1; i >= 0; i--){
        if(s[i] == '1') num[i] = num[i + 1];
        else num[i] = i;
    }
    for(int i = n; i > 0; ){
        if(i == num[max(0ll, i - m)]){
            cout << -1;
            return 0;
        }
        stk.push(i);
        i = num[max(0ll, i - m)];
    }
    while(!stk.empty()){
        cout << stk.top() - ans << ' ';
        ans = stk.top();
        stk.pop();
    }
    return 0;
}
posted @ 2026-10-27 16:24  show__code  阅读(3)  评论(0)    收藏  举报