专栏文章

题解:P14330 [JOI2021 预选赛 R2] 往返滑道 / Round Sugoroku

P14330题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minh2etj
此快照首次捕获于
2025/12/02 02:18
3 个月前
此快照最后确认于
2025/12/02 02:18
3 个月前
查看原文

P14330 [JOI2021 预选赛 R2] 往返滑道 / Round Sugoroku题解

题目传送门

算法设计
  • 核心优化思路 将 “逐秒移动” 转化为 “批量计算单次连续移动的距离”
  1. 每次移动前,确定下一个触发反弹的目标(最近的未处理 # 或边界)
  2. 直接计算当前位置到目标的距离,累加为总时间,跳过中间逐秒计算
  3. 处理目标后更新方向和未处理 # 列表,重复至所有 # 处理完毕
  • 数据结构与工具 vectorvector 存储 # 位置:收集所有初始 #1based1-based 位置,排序后方便查找
  • 二分查找:用函数"lower_bound" 快速定位 “当前方向上最近的未处理 #”,时间复杂度 (O(logM)(O(\log M)MM# 数量)
代码实现
CPP
#include <bits/stdc++.h>
using namespace std;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n, a;
    string s;
    cin >> n >> a >> s;
    
    vector<int> h;
    for (int i = 0; i < n; ++i)
        if (s[i] == '#') h.push_back(i + 1);  // 转为1-based位置
    
    int m = h.size();
    sort(h.begin(), h.end());
    long long t = 0;
    int p = a, d = 1, l = 0, r = n + 1;
    
    while (m) {
        if (d == 1) {  // 向右移动
            auto it = lower_bound(h.begin(), h.end(), p);
            int nh = (it != h.end()) ? *it : r + 1;
            int np = min(nh, r);
            
            t += np - p;
            p = np;
            
            if (p == r) d = -1;
            else { h.erase(it); m--; d = -1; }
        } else {  // 向左移动
            auto it = lower_bound(h.begin(), h.end(), p);
            int nh = (it != h.begin()) ? *prev(it) : l - 1;
            int np = max(nh, l);
            
            t += p - np;
            p = np;
            
            if (p == l) d = 1;
            else { h.erase(prev(it)); m--; d = 1; }
        }
    }
    
    cout << t << '\n';
    return 0;
}
复杂度分析
  1. 时间复杂度:O(MlogM)O(M \log M)
  • 收集 # 位置:O(N)O(N)
  • 排序 # 列表:O(MlogM)O(M \log M)
  • 处理每个 #:每次二分查找 O(logM)O(\log M)
  • vectorvector eraseerase 操作 O(1)O(1),因为每个 # 仅删除一次),总 O(MlogM)O(M \log M) (MN)(M \leq N),故整体满足 N2e5N \leq 2e5 的约束)
  1. 空间复杂度:(O(M)(O(M),仅存储 # 位置的 vectorvector

评论

0 条评论,欢迎与作者交流。

正在加载评论...