1 条题解
-
0
问题重述
平面上有 个互不相同的点(史莱姆的位置)。Megumin 需要选择一个圆,使得圆内或圆上的点数不少于 。圆的代价为其面积。求最小代价。
关键条件:题目保证“没有三只史莱姆位于同一个圆上”(no three slimes lie on the same circle)。这个条件看似不起眼,实际上暗示了一个极强的结论:所有点共线。下面我们解释这一点。
核心观察:所有点共线
为什么“无三点共圆”会导致共线?
在平面上任取三个不共线的点,它们唯一确定一个外接圆。如果不存在三个不同的点位于同一个圆上,那么就不可能存在三个不共线的点——因为一旦有不共线的三点,它们的外接圆上就恰好有这三点,与条件矛盾。
因此,整个点集必定满足:任何三个点都共线。由几何基本事实,这意味着所有点都在同一条直线上。
结论
问题转化为:在一条直线上的 个点中,找一个长度最短的区间,使其包含至少 个点。对应的最小圆以该区间为直径(即圆心在区间中点,半径为区间长度的一半),面积为 ,其中 是区间两端点的距离。
算法设计
- 排序
将所有点按照坐标排序。由于所有点共线,我们只需按它们在直线上的位置排序。标准做法是使用字典序(先 后 ),因为共线点的坐标满足线性关系,字典序等价于沿着直线的顺序。 - 滑动窗口求最小 点跨度
排序后,枚举所有长度为 的连续子段(窗口),窗口内第一个点和最后一个点之间的距离即为包含这 个点的最小区间的直径。
设窗口左端点为 ,右端点为 ,距离 。取最小值 。 - 计算面积
最小圆的半径为 ,面积为 。
注意:浮点数输出需保留足够精度,题目要求相对/绝对误差不超过 。
正确性证明
- 包含至少 个点的最小圆,在其直径上必然有两个端点都是给定点。对于共线点集,任何区间 ( 为输入点)都可以作为直径构造一个圆,该圆包含区间内所有点。若一个圆包含了某 个点,其直径至少为这 个点中相距最远的两点距离。因此,只需考虑以排序后连续 个点的首尾距离为直径的圆,取最小值即可。
- 滑动窗口的正确性来自区间贪心:对于一维点集,包含 个点的最短区间两端点必定是点集中的点,且区间内的点必然是排序后的连续 个点(否则可缩小区间)。因此,最小直径必然在某个长度为 的连续子段的首尾距离中取得。
#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
- 上传者