UVa 10619 Advanced Causal Measurements

题目描述

在一维宇宙中,我们观测到 nnn 个事件,每个事件 eie_iei 由时间 tit_iti 和位置 xix_ixi 表示,坐标均为整数,范围在 [−106,106][-10^6, 10^6][106,106] 之间。我们知道这些事件是由 mmm 个“原因”事件引起的。原因事件 cj=(Tj,Xj)c_j = (T_j, X_j)cj=(Tj,Xj) 未知,但满足因果条件:如果原因 cjc_jcj 可以导致观测事件 eie_iei ,那么必须满足

ti≥Tj+∣xi−Xj∣ t_i \ge T_j + |x_i - X_j| tiTj+xiXj

即信号从原因事件传播到观测事件的时间不超过光速(光速设为 111 )。

题目要求我们找出 最早的那个原因事件 可能发生的最晚时间 Tmin⁡T_{\min}Tmin 。也就是说,我们需要找到一个最大的 TTT ,使得存在 mmm 个原因事件,它们的时间都不早于 TTT ,并且每个观测事件至少能被其中一个原因事件“覆盖”(即满足上述因果不等式)。

题目分析

首先,将因果条件重写为关于原因时间 TjT_jTj 的不等式:

Tj≤ti−∣xi−Xj∣ T_j \le t_i - |x_i - X_j| TjtixiXj

如果我们要让所有原因事件的时间都不小于某个 TTT ,那么对于每个观测事件 eie_iei ,至少存在一个原因 cjc_jcj 满足:

T≤Tj≤ti−∣xi−Xj∣ T \le T_j \le t_i - |x_i - X_j| TTjtixiXj

这意味着

∣xi−Xj∣≤ti−T |x_i - X_j| \le t_i - T xiXjtiT

也就是说,原因事件 cjc_jcj 的位置 XjX_jXj 必须落在以 xix_ixi 为中心、半径为 ti−Tt_i - TtiT 的区间内:

Xj∈[xi−(ti−T), xi+(ti−T)] X_j \in [x_i - (t_i - T),\ x_i + (t_i - T)] Xj[xi(tiT), xi+(tiT)]

我们把这个区间记为 Ii(T)I_i(T)Ii(T)

注意 :如果 ti−T<0t_i - T < 0tiT<0 ,那么这个区间是空的,表示事件 eie_iei 无法被任何时间 ≥T\ge TT 的原因覆盖。在这种情况下, TTT 不可行。

于是,问题转化为:

给定 nnn 个区间 Ii(T)I_i(T)Ii(T) ,是否可以用 mmm 个点(原因的位置 XjX_jXj )覆盖所有这些区间? 这里“覆盖”指每个区间至少包含一个点。

这是一个经典的 区间覆盖点 问题,可以用贪心算法解决:

  1. 将所有区间按右端点从小到大排序。
  2. 初始化点集为空,遍历排序后的区间:
    • 如果当前区间已经包含之前放置的某个点,则跳过。
    • 否则,在当前区间的右端点放置一个新的点,并计数。
  3. 如果最终放置的点数不超过 mmm ,则可行;否则不可行。

现在,我们需要找到最大的 TTT 使得上述覆盖问题有解。由于 TTT 越大,区间 Ii(T)I_i(T)Ii(T) 越窄(半径 ti−Tt_i - TtiT 越小),覆盖难度越大,因此 TTT 具有单调性。我们可以使用二分查找来寻找最大的可行 TTT

二分查找范围

  • 下界 lowlowlow :可以取一个足够小的数,例如 −2×109-2 \times 10^92×109 ,确保所有 ti−Tt_i - TtiT 都很大,区间很容易覆盖。
  • 上界 highhighhigh :不能超过所有 tit_iti 的最小值,因为如果 T>min⁡tiT > \min t_iT>minti ,那么对于 tit_iti 最小的事件, ti−T<0t_i - T < 0tiT<0 ,区间为空,无法覆盖。因此上界可取 high=min⁡tihigh = \min t_ihigh=minti

二分查找过程中,对于每个 midmidmid ,计算所有区间,并用贪心算法判断是否能用 mmm 个点覆盖。如果可以,说明 TTT 可以更大(或至少保持 midmidmid),则尝试右半区间;否则尝试左半区间。

时间复杂度

  • 每次检查需要 O(nlog⁡n)O(n \log n)O(nlogn) 时间排序区间,贪心遍历 O(n)O(n)O(n)
  • 二分查找需要 O(log⁡(坐标范围))O(\log(\text{坐标范围}))O(log(坐标范围)) 次迭代,坐标范围约为 4×1064 \times 10^64×106log⁡\loglog 约为 222222
  • 总复杂度 O(nlog⁡nlog⁡R)O(n \log n \log R)O(nlognlogR) ,对于 n≤105n \le 10^5n105 可以接受。

算法步骤

  1. 读取所有观测事件 (ti,xi)(t_i, x_i)(ti,xi) ,并记录 min⁡ti\min t_iminti
  2. 在范围 [low,high][low, high][low,high] 内二分查找 TTT
  3. 对于每个候选 TTT
    • 计算每个事件的区间 Ii(T)=[xi−(ti−T), xi+(ti−T)]I_i(T) = [x_i - (t_i - T),\ x_i + (t_i - T)]Ii(T)=[xi(tiT), xi+(tiT)]
    • 如果某个 ti−T<0t_i - T < 0tiT<0 ,直接判定不可行。
    • 将所有区间按右端点升序排序。
    • 贪心放置点:遍历区间,若当前区间左端点大于上一个放置的点,则在该区间右端点放置新点。
    • 如果放置点数 ≤m\le mm ,则可行;否则不可行。
  4. 输出二分找到的最大可行 TTT

代码实现

// Advanced Causal Measurements
// UVa ID: 10619
// Verdict: Accepted
// Submission Date: 2025-12-05
// UVa Run Time: 0.920s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

struct Event {
    int t, x;
};

// 检查对于给定的 earliestTime,是否能用 m 个原因覆盖所有事件
bool canCover(int earliestTime, const vector<Event>& events, int m) {
    vector<pair<long long, long long>> intervals; // 区间 [L, R]
    for (const auto& e : events) {
        long long radius = e.t - earliestTime;
        if (radius < 0) return false; // 该事件无法被任何时间>=earliestTime的原因覆盖
        intervals.push_back({e.x - radius, e.x + radius});
    }

    // 按右端点升序排序
    sort(intervals.begin(), intervals.end(), [](const pair<long long, long long>& a, const pair<long long, long long>& b) {
        return a.second < b.second;
    });

    int points = 0;
    long long lastPoint = -1LL << 60; // 上一个放置的点
    for (const auto& interval : intervals) {
        if (interval.first > lastPoint) { // 当前区间未被覆盖
            lastPoint = interval.second; // 在右端点放点
            points++;
            if (points > m) return false;
        }
    }
    return points <= m;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);

    int caseCount;
    cin >> caseCount;
    for (int caseIdx = 1; caseIdx <= caseCount; caseIdx++) {
        int n, m;
        cin >> n >> m;
        vector<Event> events(n);
        int minT = 1000000000;
        for (int i = 0; i < n; i++) {
            cin >> events[i].t >> events[i].x;
            minT = min(minT, events[i].t);
        }

        long long low = -2000000000, high = minT;
        long long answer = low;
        while (low <= high) {
            long long mid = (low + high) / 2;
            if (canCover(mid, events, m)) {
                answer = mid;
                low = mid + 1;
            } else
                high = mid - 1;
        }
        cout << "Case " << caseIdx << ": " << answer << "\n";
    }
    return 0;
}

总结

本题的核心在于问题转化:将物理中的因果条件转化为几何区间覆盖问题,再通过二分查找和贪心算法求解。主要步骤包括:

  1. 推导区间:从因果不等式得到每个观测事件对应的原因位置区间。
  2. 二分答案:由于最早原因时间 TTT 具有单调性,二分查找最大可行值。
  3. 贪心覆盖:对于给定的 TTT ,用经典贪心算法判断是否能用 mmm 个点覆盖所有区间。

注意使用 long long 类型避免整数溢出,并合理设置二分上下界。最终算法高效且易于实现,能够处理大规模输入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值