【题目链接】
【思路要点】
- 若知道环上某个点的最终位置,那么二分答案+最大流即可解决问题。
- 因此,对环上第一个点的位置进行爬山。
- 我们发现在大部分时候最优解是关于某个点的最终位置的周期函数,并且每个周期是单峰的,因此爬山在大部分时候是有正确性保证的。
- 总时间复杂度\(O(Cnt*LogV*Dinic(N,N^2))\),其中\(Cnt\)为爬山次数。
- 注:由于时间限制较紧,部分测试点的\(Cnt\)可能取的较小,导致精度无法通过少数测试点。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 405; const double pi = acos(-1); const double delta = 0.5; const double eps = 1e-12; const double INF = 1e100; template <typename T> void read(T &x) { x = 0; int f = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -f; for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0'; x *= f; } template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } struct edge {int dest, flow; unsigned home; }; vector <edge> a[MAXN]; int s, t, dist[MAXN]; unsigned curr[MAXN]; int n, r, x[MAXN], y[MAXN]; int dinic(int pos, int limit) { if (pos == t) return limit; int used = 0, tmp; for (unsigned &i = curr[pos]; i < a[pos].size(); i++) if (a[pos][i].flow != 0 && dist[pos] + 1 == dist[a[pos][i].dest] && (tmp = dinic(a[pos][i].dest, min(limit - used, a[pos][i].flow))) != 0) { used += tmp; a[pos][i].flow -= tmp; a[a[pos][i].dest][a[pos][i].home].flow += tmp; if (used == limit) return used; } return used; } bool bfs() { static int q[MAXN], l, r; for (int i = 0; i <= t; i++) dist[i] = 0; dist[s] = 1, q[l = r = 0] = s; while (l <= r) { int tmp = q[l++]; for (unsigned i = 0; i < a[tmp].size(); i++) if (dist[a[tmp][i].dest] == 0 && a[tmp][i].flow != 0) { dist[a[tmp][i].dest] = dist[tmp] + 1; q[++r] = a[tmp][i].dest; } } return dist[t] != 0; } void addedge(int s, int t) { a[s].push_back((edge) {t, 1, a[t].size()}); a[t].push_back((edge) {s, 0, a[s].size() - 1}); } double dis(double sx, double sy, double tx, double ty) { return sqrt((sx - tx) * (sx - tx) + (sy - ty) * (sy - ty)); } double f(double val) { static double px[MAXN], py[MAXN]; double delta = 2 * acos(-1) / n; for (int i = 1; i <= n; i++) { px[i] = sin(val) * r; py[i] = cos(val) * r; val += delta; } double l = 0, r = 0; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) chkmax(r, dis(px[i], py[i], x[j], y[j])); while (l + eps < r) { double mid = (l + r) / 2; s = 0, t = n + n + 1; for (int i = s; i <= t; i++) a[i].clear(); for (int i = 1; i <= n; i++) { addedge(s, i); addedge(i + n, t); for (int j = 1; j <= n; j++) if (dis(px[i], py[i], x[j], y[j]) <= mid) addedge(i, j + n); } int flow = 0; while (bfs()) { memset(curr, 0, sizeof(curr)); flow += dinic(s, 1e9); } if (flow == n) r = mid; else l = mid; } return (l + r) / 2; } int main() { read(n), read(r); for (int i = 1; i <= n; i++) read(x[i]), read(y[i]); double now = 0, stp = 2 * pi / n, ans = f(0); int cnt = 0; while (stp >= eps) { stp *= delta; double tmp = now + stp, tnp = f(tmp); if (tnp < ans - eps) { ans = tnp; now = tmp; } tmp = now - stp, tnp = f(tmp); if (tnp < ans - eps) { ans = tnp; now = tmp; } cnt += n * n * sqrt(n); if (n <= 115) cnt = 0; if (cnt >= 3e6) { printf("%.10lf\n", ans); return 0; } } printf("%.10lf\n", ans); return 0; }