1 条题解

  • 0
    @ 2026-5-3 16:12:10

    问题重述

    平面上有 nn 个互不相同的点(史莱姆的位置)。Megumin 需要选择一个圆,使得圆内或圆上的点数不少于 kk。圆的代价为其面积。求最小代价。

    关键条件:题目保证“没有三只史莱姆位于同一个圆上”(no three slimes lie on the same circle)。这个条件看似不起眼,实际上暗示了一个极强的结论:所有点共线。下面我们解释这一点。

    核心观察:所有点共线

    为什么“无三点共圆”会导致共线?

    在平面上任取三个不共线的点,它们唯一确定一个外接圆。如果不存在三个不同的点位于同一个圆上,那么就不可能存在三个不共线的点——因为一旦有不共线的三点,它们的外接圆上就恰好有这三点,与条件矛盾。

    因此,整个点集必定满足:任何三个点都共线。由几何基本事实,这意味着所有点都在同一条直线上。

    结论

    问题转化为:在一条直线上的 nn 个点中,找一个长度最短的区间,使其包含至少 kk 个点。对应的最小圆以该区间为直径(即圆心在区间中点,半径为区间长度的一半),面积为 πd24\pi \cdot \frac{d^2}{4},其中 dd 是区间两端点的距离。

    算法设计

    1. 排序
      将所有点按照坐标排序。由于所有点共线,我们只需按它们在直线上的位置排序。标准做法是使用字典序(先 xxyy),因为共线点的坐标满足线性关系,字典序等价于沿着直线的顺序。
    2. 滑动窗口求最小 kk 点跨度
      排序后,枚举所有长度为 kk 的连续子段(窗口),窗口内第一个点和最后一个点之间的距离即为包含这 kk 个点的最小区间的直径。
      设窗口左端点为 sis_i,右端点为 si+k1s_{i+k-1},距离 di=dist(si,si+k1)d_i = \text{dist}(s_i, s_{i+k-1})。取最小值 dmin=min1ink+1did_{\min} = \min_{1 \le i \le n-k+1} d_i
    3. 计算面积
      最小圆的半径为 r=dmin/2r = d_{\min} / 2,面积为 πr2=πdmin24\pi r^2 = \pi \cdot \frac{d_{\min}^2}{4}

    注意:浮点数输出需保留足够精度,题目要求相对/绝对误差不超过 10610^{-6}

    正确性证明

    • 包含至少 kk 个点的最小圆,在其直径上必然有两个端点都是给定点。对于共线点集,任何区间 [p,q][p, q]p,qp, q 为输入点)都可以作为直径构造一个圆,该圆包含区间内所有点。若一个圆包含了某 kk 个点,其直径至少为这 kk 个点中相距最远的两点距离。因此,只需考虑以排序后连续 kk 个点的首尾距离为直径的圆,取最小值即可。
    • 滑动窗口的正确性来自区间贪心:对于一维点集,包含 kk 个点的最短区间两端点必定是点集中的点,且区间内的点必然是排序后的连续 kk 个点(否则可缩小区间)。因此,最小直径必然在某个长度为 kk 的连续子段的首尾距离中取得。
    #include <bits/stdc++.h>
    using namespace std;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        int n, k;
        cin >> n >> k;
        vector<pair<int, int>> pts(n);
        for (int i = 0; i < n; ++i) {
            cin >> pts[i].first >> pts[i].second;
        }
    
        // 按字典序排序:先 x 后 y
        sort(pts.begin(), pts.end());
    
        double min_d = 1e30;   // 足够大的初值
        for (int i = 0; i + k - 1 < n; ++i) {
            long long dx = (long long)pts[i].first - pts[i + k - 1].first;
            long long dy = (long long)pts[i].second - pts[i + k - 1].second;
            double d = sqrt((long double)dx * dx + (long double)dy * dy);
            if (d < min_d) min_d = d;
        }
    
        double r = min_d / 2.0;
        double area = r * r * M_PI;   // 或 acos(-1.0)
    
        cout << fixed << setprecision(10) << area << '\n';
        return 0;
    }
    
    • 1

    信息

    ID
    6752
    时间
    3000ms
    内存
    256MiB
    难度
    8
    标签
    递交数
    2
    已通过
    1
    上传者