专栏文章

题解:P14222 [ICPC 2024 Kunming I] 收集硬币

P14222题解参与者 3已保存评论 3

文章操作

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

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

P14222 [ICPC 2024 Kunming I] 收集硬币 题解

题目大意

10910^9 个格子排成一行,两个机器人以相同的最大速度 vv 移动。有 nn 枚硬币在第 tit_i 秒出现在格子 cic_i ,如果此时有机器人在该格子就能收集硬币。求能收集所有硬币的最小速度 vv ,如果不可能输出 1-1

思路

我们发现速度 vv 具有单调性:如果某个速度可行,那么更大的速度一定也可行。因此可以用二分查找来求最小速度。
对于每个二分的速度 midmid,我们需要验证是否存在两个机器人的移动方案能够收集所有硬币。

验证算法

我们维护两个机器人的可能位置范围:
  • 设参考点 pp 表示最近一次确定位置的硬币
  • 区间 [l,r][l, r] 表示另一个机器人当前可能的位置范围
对于每个新的硬币 ii
  1. 计算时间差 d=(titp)×midd = (t_i - t_p) \times mid,即机器人最大移动距离
  2. 判断两个机器人能否到达当前硬币位置:
    • 参考点机器人能到达:cpdcicp+dc_p - d \leq c_i \leq c_p + d
    • 另一个机器人能到达:ldcir+dl - d \leq c_i \leq r + d
根据判断结果更新状态:
  • 两个都能到达:更新区间为交集,pp 移到当前点
  • 只有参考点能到达:扩展区间,pp 移到当前点
  • 只有另一个能到达:重置区间,pp 移到当前点
  • 都不能到达:当前速度不可行

代码实现

CPP
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 5;

int n;
struct node {
    int v, t;  // v: 位置坐标, t: 时间
} a[N]; 

// 按时间排序
bool cmp(node x, node y) {
    return x.t < y.t;
}

// 检查给定速度mid是否可行
bool check(int mid) {
    int p = 1;           // 参考点索引,表示最近确定位置的硬币
    int l = 1, r = 1e9;  // 另一个机器人的可能位置区间[l, r]
    
    for (int i = 2; i <= n; i++) {
        // 计算时间差对应的最大移动距离
        int d = (a[i].t - a[p].t) * mid;
        
        // 判断两个机器人是否能到达当前硬币位置
        bool x1 = (a[p].v + d >= a[i].v) & (a[p].v - d <= a[i].v);  // 参考点机器人能到达
        bool x2 = (r + d >= a[i].v) & (l - d <= a[i].v);            // 另一个机器人能到达
        
        // 根据两个机器人的可达性进行状态转移
        if (x1 && x2) {
            // 两个机器人都能到达:取交集更新区间
            l = min(a[p].v - d, l - d);
            r = max(a[p].v + d, r + d);
            p = i;  // 更新参考点为当前点
        } else if (x1) {
            // 只有参考点机器人能到达:扩展另一个机器人的区间
            p = i;
            l = l - d;  // 区间左边界扩展
            r = r + d;  // 区间右边界扩展
        } else if (x2) {
            // 只有另一个机器人能到达:重置区间
            l = a[p].v - d;  // 以参考点为中心重置左边界
            r = a[p].v + d;  // 以参考点为中心重置右边界
            p = i;           // 更新参考点为当前点
        } else {
            // 两个机器人都无法到达当前硬币
            return 0;  // 当前速度不可行
        }
        
        // 确保区间在有效范围内[1, 10^9]
        l = max(l, 1ll);
        r = min(r, 1000000000ll);
    }
    return 1;  // 所有硬币都能被收集
}

void solve() {
    int ans = -1;
    cin >> n;
    // 读入硬币数据
    for (int i = 1; i <= n; i++) {
        cin >> a[i].t >> a[i].v;
    }
    // 按时间排序硬币
    sort(a + 1, a + n + 1, cmp);
    
    // 二分查找最小速度
    int l = 0, r = 1e9, mid;
    while (l < r) {
        mid = l + r >> 1;  // 取中间速度
        if (check(mid)) {
            ans = mid;     // 当前速度可行,尝试更小的速度
            r = mid;
        } else {
            l = mid + 1;   // 当前速度不可行,需要更大的速度
        }
    }	
    cout << ans << "\n";
}

signed main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}
这道题的时间复杂度是:O(T(nlogn+nlog109))O(T (n \log n + n \log 10^9))
  • 排序:O(nlogn)O(n \log n)
  • 二分查找:O(log109)O(\log 10^9)
  • check 验证:O(n)O(n)
3 秒的时限应该是可以过的

注意事项

  1. 排序:硬币必须按时间排序,因为机器人移动依赖于时间顺序
  2. 区间维护:使用 [l,r][l, r] 区间表示另一个机器人的可能位置,避免精确跟踪具体位置
  3. 边界处理:每次更新区间后要限制在 [1,109][1, 10^9] 范围内
  4. 状态转移:根据两个机器人的可达性采用不同的更新策略,确保至少有一个机器人能到达每个硬币位置
感谢阅读。

评论

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

正在加载评论...