【BZOJ5316】【JSOI2018】绝地反击

【题目链接】

【思路要点】

  • 若知道环上某个点的最终位置,那么二分答案+最大流即可解决问题。
  • 因此,对环上第一个点的位置进行爬山。
  • 我们发现在大部分时候最优解是关于某个点的最终位置的周期函数,并且每个周期是单峰的,因此爬山在大部分时候是有正确性保证的。
  • 总时间复杂度\(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;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80434149