题解:洛谷 P9125 Cow-libi

【题目来源】

洛谷:P9125 [USACO23FEB] Cow-libi S - 洛谷

【题目描述】

注意:本题的时间限制为 4 秒,是默认限制的两倍。

有人在 Farmer John 的 \(G(1 \leq G \leq 10^5)\) 个私人花园里偷吃了庄稼!通过他的专业法医知识,Farmer John 确定了每个花园被偷吃的具体时间。他还发现,这些事件的罪魁祸首是一头单独的奶牛。

为了回应这些犯罪行为,Farmer John 的 \(N(1 \leq N \leq 10^5)\) 头奶牛每头都提供了一个不在作案现场的证明(即“不在场证明”),表明奶牛在特定时间出现在特定位置。请帮助 Farmer John 判断这些“不在场证明”中哪些能够证明奶牛的清白。

如果一头奶牛无法在她的“不在场证明”位置与所有被偷吃地点之间往返,则可以确定这头奶牛是清白的。奶牛的移动速度为每单位时间 \(1\) 单位距离。本题中提到的距离均为欧几里得距离。

【输入】

第一行包含两个用空格分隔的整数 \(G\)\(N\)

接下来的 \(G\) 行,每行包含三个用空格分隔的整数 \(x, y, t\)\(-10^9 \leq x, y \leq 10^9; 0 \leq t \leq 10^9\)),描述某次偷吃事件的地点和时间。可以保证单独一头奶牛可以在所有被偷吃地点之间往返。

接下来的 \(N\) 行,每行包含 \(x, y, t\)\(-10^9 \leq x, y \leq 10^9; 0 \leq t \leq 10^9\)),描述某头奶牛的“不在场证明”中提到的位置和时间。

【输出】

输出一个整数,表示能够证明清白的奶牛的数量。

【输入样例】

2 4
0 0 100
50 0 200
0 50 50
1000 1000 0
50 0 200
10 0 170

【输出样例】

2

【核心思想】

  1. 问题分析:给定 \(G\) 个偷吃事件的时空坐标 \((x_i, y_i, t_i)\)\(N\) 头奶牛的不在场证明 \((cowx, cowy, cowt)\)。奶牛速度为 \(1\),若某奶牛无法从证明位置在时间内到达任一偷吃地点,则证明其清白。本质上是几何 + 二分查找问题:判断奶牛与偷吃事件时空点之间的距离是否大于时间差。

  2. 算法选择

    • 按时间排序:将偷吃事件按时间 \(t\) 排序
    • 二分定位:对每头奶牛,二分找到其证明时间 \(cowt\) 在偷吃事件时间序列中的位置
    • 距离判定:只需检查 \(cowt\) 前后最近的两个偷吃事件,若均无法到达则奶牛清白
  3. 关键步骤

    • 读取数据:读入 \(G, N\)\(G\) 个偷吃事件,按时间排序
    • 处理每头奶牛
      • \(cowt < g[1].t\):检查是否能到达最早偷吃点,不能则 \(ans \leftarrow ans + 1\)
      • \(cowt > g[G].t\):检查是否能到达最晚偷吃点,不能则 \(ans \leftarrow ans + 1\)
      • 否则二分找到最大的 \(res\) 使得 \(g[res].t < cowt\)
      • 检查奶牛是否能到达 \(g[res]\)\(g[res+1]\)
        • 欧几里得距离平方 \(> (time\_diff)^2\) 则无法到达
        • 若至少一个无法到达,innocent = true\(ans \leftarrow ans + 1\)
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(G \log G + N \log G)\),排序 + 每头奶牛二分
    • 空间复杂度:\(O(G)\),存储偷吃事件
  5. 几何 + 二分查找的核心思想

    • 时间单调性:偷吃事件按时间排序后,奶牛若无法到达 \(cowt\) 前后最近的两个事件,则无法到达任何事件(因为其他事件在时间上更远)
    • 欧几里得距离判定:速度为 \(1\),可到达当且仅当 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2} \leq |t_1-t_2|\),为避免浮点运算比较距离平方与时间差平方
    • 边界处理\(cowt\) 在所有事件之前或之后的情况单独处理,避免二分越界
    • 清白的充分性:只需存在一个偷吃事件无法到达即可证明清白,但检查最近两个事件已足够
    • 适用于时空分析、最近点查询、不在场证明类问题

【解题思路】

【算法标签】

普及 #整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long  // 因为题目中计算后会超int,所以全部修改为long long
int G, n, ans;
struct node {
    int x, y, t;
    bool operator < (node other) {  // 后面用到sor排序,重载"<"
        return t < other.t;
    }
}g[100005];
signed main()  // 前面有define,所以这里改为signed
{
    cin >> G >> n;  // 输入G和n
    for (int i=1; i<=G; i++) {  // 输入G个吃草的点
        cin >> g[i].x >> g[i].y >> g[i].t;
    }
    sort(g+1, g+G+1);  // 所有的点按照从小到大排序
    int cowx, cowy, cowt;
    for (int i=1; i<=n; i++) {  // 输入每只奶牛的证词
        cin >> cowx >> cowy >> cowt;
        int res=0;  // 定义二分答案
        if (cowt<g[1].t) {  // 如果cowt时刻小于g[1].t
            if ((g[1].x-cowx)*(g[1].x-cowx)+(g[1].y-cowy)*(g[1].y-cowy) > (g[1].t-cowt)*(g[1].t-cowt)) {  // 如果它没有足够的时间到达g[1]
                ans++;  // 就是无辜的
            }
        } else if (cowt>g[G].t) {  // 如果cowt的时刻大于g[G].t
            if ((g[G].x-cowx)*(g[G].x-cowx)+(g[G].y-cowy)*(g[G].y-cowy) > (g[G].t-cowt)*(g[G].t-cowt)) {  // 如果它没有足够的时间到达g[G]
                ans++;  // 也是无辜的
            }
        } else {
            int l = 1, r = G;
            while (l<=r) {  // 二分答案模板
                int mid = (l+r)/2;
                if (g[mid].t<cowt) {  // g[mid].t小于cowt时(其他模板代码固定,可以开调试观察这里的结果)
                    res = mid;  // 就更新res
                    l = mid+1;  // 然后增加l,找到最大的l
                } else {  // 否则
                    r = mid-1;  // 减少r
                }
            }
            bool innocent = false;  // 定义标记位
            if ((g[res].x-cowx)*(g[res].x-cowx) + (g[res].y-cowy)*(g[res].y-cowy) > (g[res].t-cowt)*(g[res].t-cowt)) {  // 如果无法到res这个点
                    innocent = true;  // 修改标记位
            }
            if ((g[res+1].x-cowx)*(g[res+1].x-cowx) + (g[res+1].y-cowy)*(g[res+1].y-cowy) > (g[res+1].t-cowt)*(g[res+1].t-cowt)) {  // 如果无法到res+1这个点
                innocent = true;  // 修改标记位
            }
            ans += innocent;  // 只要有一个吃草点无法到达,就是无辜的
        }
    }
    cout << ans << endl;  // 输出无辜的奶牛数量
    return 0;
}

【运行结果】

2 4
0 0 100
50 0 200
0 50 50
1000 1000 0
50 0 200
10 0 170
2
posted @ 2026-08-17 22:49  团爸讲算法  阅读(5)  评论(0)    收藏  举报