2025 CCPC 北京市赛 E 题 题解
· 6 min read
题目
https://codeforces.com/gym/105851/problem/E
2025 CCPC 北京市赛 E 题 布置 WAP
题目大意
平面上有 n 个点和一条直线。在直线上找一个点,使得该点到离它最远的点的距离最小。
思路
-
参数化直线
设直线方程为Ax + By + C = 0。取直线上一点t,方向向量u = {B, -A}。
直线上任意一点可表示为Q(s) = t + s * u,其中s为实数参数。 -
二分答案
对当前答案r,以每个点为圆心画半径r的圆。
判断直线上是否存在一个点被所有圆覆盖(即直线与所有圆的公共交集非空)。 -
区间求交
对每个圆,求其与直线的交点,得到参数区间[s - dt, s + dt]。
对所有区间求交集,若最终区间非空,则答案r可行。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
#define dbg(x) cerr << #x << " = " << (x) << endl;
#define vdbg(a) cout << #a << " = "; for (auto x : a) cout << x << " "; cout << endl;
struct Point { db x, y; };
using Vec = Point;
db eps = 1e-9;
struct Line { Point p; Vec v; };
struct Circle { Point O; db r; };
bool operator<(Point a, Point b) {
return a.x == b.x ? a.x < b.y : a.x < b.x;
}
Vec operator-(Vec u, Vec v) {
return {u.x - v.x, u.y - v.y};
}
Vec operator+(Vec u, Vec v) {
return {u.x + v.x, u.y + v.y};
}
Vec operator*(db k, Vec v) {
return {k * v.x, k * v.y};
}
db len(Vec u) {
return sqrt(u.x * u.x + u.y * u.y);
}
db dot(Vec u, Vec v) {
return u.x * v.x + u.y * v.y;
}
// 点到直线的垂足
Point pedal(Point p, Line l) {
return l.p - dot((l.p - p), l.v) / dot(l.v, l.v) * l.v;
}
// 直线与圆的交点(参数化形式)
vector<Point> inter(Line l, Circle C) {
Point p = pedal(C.O, l);
db h = len(p - C.O);
if (h - C.r > eps) return {};
if (fabs(h - C.r) < eps) return {p};
db d = sqrt(C.r * C.r - h * h);
Vec u = d / len(l.v) * l.v;
return {p + u, p - u};
}
void solve() {
int n; cin >> n;
vector<Point> p(n);
for (int i = 0; i < n; ++i) {
db a, b; cin >> a >> b;
p[i] = {a, b};
}
db A, B, C; cin >> A >> B >> C;
Vec u = {B, -A};
Line L; Point t;
if (A) t = {-C / A, 0};
else t = {0, -C / B};
L = {t, u};
db q = sqrt(A * A + B * B);
auto check = [&](db r) {
db st = -1e10, ed = 1e10;
for (int i = 0; i < n; ++i) {
db d = fabs(A * p[i].x + B * p[i].y + C) / q;
if (d > r + eps) return 0;
db pp = dot(p[i] - t, u) / (q * q);
db dt = sqrt(r * r - d * d) / q;
st = max(st, pp - dt), ed = min(ed, pp + dt);
if (st > ed + eps) return 0;
}
return 1;
};
db l = 0, r = 1e10, ans;
while (r - l > eps) {
db mid = (l + r) * 0.5;
if (check(mid)) {
ans = mid;
r = mid;
} else l = mid;
}
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int T = 1;
cin >> T;
while (T--) solve();
return 0;
}
`