https://atcoder.jp/contests/abc181/tasks
F - Silver Woods
题意:二维平面上有上边界 y=100 和下边界 y=−100,在两个边界中间还有一些点,有一个圆要从 (−109,0) 移动到 (109,0),可以沿任何方向移动,但是不能穿过点或者边界,问最大可行的圆的半径。
二分+并查集
二分答案。对于一个已知半径的圆,要判断能否成功移动到目标。
在刘汝佳平面图最小割那部分有一道叫动物园啥的题,讲的就是判断能否从起点到达目标点。有一个很妙的方法。
遍历所有点,如果到上边界距离小于圆的直径则该点向上边界连边,下边界类似。对于一对点,如果它们距离小于直径,则连边。
判断如果上边界存在路径到达下边界,则说明这一个带形区域被分割成了两块,这两块完全不互通,那么这个圆就没法到达目标点。
否则一定能够到达,实际上甚至可以到达任意点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std; #define ll long long const int N = 2e6 + 10; const int INF = 0x3f3f3f3f; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 998244353; const double eps = 1e-10; int n; double x[N], y[N]; int par[N]; void init(int n) { for (int i = 1; i <= n; i++)par[i] = i; } int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); } void uni(int x, int y) { par[find(x)] = find(y); } bool ck(double r) { init(n + 2); for (int i = 1; i <= n; i++) { if (100 - y[i] < 2*r)uni(i, n + 1); if (y[i] + 100 < 2*r)uni(i, n + 2); for (int j = i + 1; j <= n; j++) { if ((x[i] - x[j])*(x[i] - x[j]) + (y[i] - y[j])*(y[i] - y[j]) < 4 * r*r)uni(i, j); } } if (find(n + 1) == find(n + 2))return false; else return true; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lf%lf", &x[i], &y[i]); } double L = 0, R = 100; while (R - L > eps) { double mid = (L + R) / 2; if (ck(mid))L = mid; else R = mid; } printf("%.9lf\n", L); return 0; }
|