题目描述
在一维宇宙中,我们观测到 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| ti≥Tj+∣xi−Xj∣
即信号从原因事件传播到观测事件的时间不超过光速(光速设为 111 )。
题目要求我们找出 最早的那个原因事件 可能发生的最晚时间 TminT_{\min}Tmin 。也就是说,我们需要找到一个最大的 TTT ,使得存在 mmm 个原因事件,它们的时间都不早于 TTT ,并且每个观测事件至少能被其中一个原因事件“覆盖”(即满足上述因果不等式)。
题目分析
首先,将因果条件重写为关于原因时间 TjT_jTj 的不等式:
Tj≤ti−∣xi−Xj∣ T_j \le t_i - |x_i - X_j| Tj≤ti−∣xi−Xj∣
如果我们要让所有原因事件的时间都不小于某个 TTT ,那么对于每个观测事件 eie_iei ,至少存在一个原因 cjc_jcj 满足:
T≤Tj≤ti−∣xi−Xj∣ T \le T_j \le t_i - |x_i - X_j| T≤Tj≤ti−∣xi−Xj∣
这意味着
∣xi−Xj∣≤ti−T |x_i - X_j| \le t_i - T ∣xi−Xj∣≤ti−T
也就是说,原因事件 cjc_jcj 的位置 XjX_jXj 必须落在以 xix_ixi 为中心、半径为 ti−Tt_i - Tti−T 的区间内:
Xj∈[xi−(ti−T), xi+(ti−T)] X_j \in [x_i - (t_i - T),\ x_i + (t_i - T)] Xj∈[xi−(ti−T), xi+(ti−T)]
我们把这个区间记为 Ii(T)I_i(T)Ii(T) 。
注意 :如果 ti−T<0t_i - T < 0ti−T<0 ,那么这个区间是空的,表示事件 eie_iei 无法被任何时间 ≥T\ge T≥T 的原因覆盖。在这种情况下, TTT 不可行。
于是,问题转化为:
给定 nnn 个区间 Ii(T)I_i(T)Ii(T) ,是否可以用 mmm 个点(原因的位置 XjX_jXj )覆盖所有这些区间? 这里“覆盖”指每个区间至少包含一个点。
这是一个经典的 区间覆盖点 问题,可以用贪心算法解决:
- 将所有区间按右端点从小到大排序。
- 初始化点集为空,遍历排序后的区间:
- 如果当前区间已经包含之前放置的某个点,则跳过。
- 否则,在当前区间的右端点放置一个新的点,并计数。
- 如果最终放置的点数不超过 mmm ,则可行;否则不可行。
现在,我们需要找到最大的 TTT 使得上述覆盖问题有解。由于 TTT 越大,区间 Ii(T)I_i(T)Ii(T) 越窄(半径 ti−Tt_i - Tti−T 越小),覆盖难度越大,因此 TTT 具有单调性。我们可以使用二分查找来寻找最大的可行 TTT 。
二分查找范围
- 下界 lowlowlow :可以取一个足够小的数,例如 −2×109-2 \times 10^9−2×109 ,确保所有 ti−Tt_i - Tti−T 都很大,区间很容易覆盖。
- 上界 highhighhigh :不能超过所有 tit_iti 的最小值,因为如果 T>mintiT > \min t_iT>minti ,那么对于 tit_iti 最小的事件, ti−T<0t_i - T < 0ti−T<0 ,区间为空,无法覆盖。因此上界可取 high=mintihigh = \min t_ihigh=minti 。
二分查找过程中,对于每个 midmidmid ,计算所有区间,并用贪心算法判断是否能用 mmm 个点覆盖。如果可以,说明 TTT 可以更大(或至少保持 midmidmid),则尝试右半区间;否则尝试左半区间。
时间复杂度
- 每次检查需要 O(nlogn)O(n \log n)O(nlogn) 时间排序区间,贪心遍历 O(n)O(n)O(n) 。
- 二分查找需要 O(log(坐标范围))O(\log(\text{坐标范围}))O(log(坐标范围)) 次迭代,坐标范围约为 4×1064 \times 10^64×106 , log\loglog 约为 222222 。
- 总复杂度 O(nlognlogR)O(n \log n \log R)O(nlognlogR) ,对于 n≤105n \le 10^5n≤105 可以接受。
算法步骤
- 读取所有观测事件 (ti,xi)(t_i, x_i)(ti,xi) ,并记录 minti\min t_iminti 。
- 在范围 [low,high][low, high][low,high] 内二分查找 TTT 。
- 对于每个候选 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−(ti−T), xi+(ti−T)] 。
- 如果某个 ti−T<0t_i - T < 0ti−T<0 ,直接判定不可行。
- 将所有区间按右端点升序排序。
- 贪心放置点:遍历区间,若当前区间左端点大于上一个放置的点,则在该区间右端点放置新点。
- 如果放置点数 ≤m\le m≤m ,则可行;否则不可行。
- 输出二分找到的最大可行 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;
}
总结
本题的核心在于问题转化:将物理中的因果条件转化为几何区间覆盖问题,再通过二分查找和贪心算法求解。主要步骤包括:
- 推导区间:从因果不等式得到每个观测事件对应的原因位置区间。
- 二分答案:由于最早原因时间 TTT 具有单调性,二分查找最大可行值。
- 贪心覆盖:对于给定的 TTT ,用经典贪心算法判断是否能用 mmm 个点覆盖所有区间。
注意使用 long long 类型避免整数溢出,并合理设置二分上下界。最终算法高效且易于实现,能够处理大规模输入。

232

被折叠的 条评论
为什么被折叠?



