绝地反击
题目背景:
5.14 模拟 JSOI2018D1T3
分析:计算几何 + 网络流 + 二分 + 网络流退流
又是我不会的新姿势,首先我们考虑如何暴力,对于最优的时间,我们可以肯定,一定有一个飞船是恰好在这个时间到了对应坐标,而没有任何停留,否则一定可以更优,那么我们二分最大时间,然后枚举每一条飞船作为最晚到达的那一个,然后就可以确定对应的位置,然后跑一遍二分图匹配即可,这样做的话普通写大概有50 ~ 60,加上一些特判和一些剪枝大概能够有90左右,当然,这个结果是loj得到的。
时间复杂度:O(n7/2logn~ n4logn),据说用HK可以卡过去?
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
///*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
for (int i = 0; i <= 10; ++i) std::cout << obuf[i];
std::cout << '\n';
fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
if (c == '-') iosig = true;
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int MAXN = 400 + 10;
const double PI = acos(-1.0);
const double eps = 1e-8;
const int INF = MAXN;
struct node {
int to, w, rev;
node() {}
node(int to, int w, int rev) : to(to), w(w), rev(rev) {}
} ;
std::vector<node> edge[MAXN << 1];
int dis[MAXN], temp[MAXN];
inline void add_edge(int x, int y, int w) {
edge[x].push_back(node(y, w, edge[y].size()));
edge[y].push_back(node(x, 0, edge[x].size() - 1));
}
inline bool bfs(int s, int t) {
static std::queue<int> q;
memset(dis, -1, sizeof(int) * (t + 5));
memset(temp, 0, sizeof(int) * (t + 5));
dis[s] = 0, q.push(s);
while (!q.empty()) {
int cur = q.front();
q.pop();
for (int p = 0; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (e->w && dis[e->to] == -1)
dis[e->to] = dis[cur] + 1, q.push(e->to);
}
}
return dis[t] != -1;
}
inline int dfs(int cur, int low, int t) {
if (cur == t) return low;
int delta = 0;
for (int &p = temp[cur]; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (e->w && dis[e->to] == dis[cur] + 1) {
int ret = dfs(e->to, std::min(low - delta, e->w), t);
e->w -= ret, edge[e->to][e->rev].w += ret;
if ((delta += ret) == low) break ;
}
}
return delta;
}
inline int dinic(int s, int t) {
int ans = 0, ret = 0;
while (bfs(s, t)) do ret = dfs(s, INF, t); while (ans += ret, ret);
return ans;
}
struct point {
double x, y;
point() {}
point(double x, double y) : x(x), y(y) {}
inline point operator + (const point &a) const {
return point(x + a.x, y + a.y);
}
inline point operator - (const point &a) const {
return point(x - a.x, y - a.y);
}
inline point operator / (const double a) const {
return point(x / a, y / a);
}
inline point operator * (const double a) const {
return point(x * a, y * a);
}
inline double dis() {
return sqrt(x * x + y * y);
}
inline double dis2() {
return x * x + y * y;
}
inline point rotate(double ang) {
double cos_a = cos(ang), sin_a = sin(ang);
return point(x * cos_a - y * sin_a, x * sin_a + y * cos_a);
}
} p[MAXN], st[MAXN];
int n, r, x, y;
inline point get_pos(point p, double dis, double flag) {
double a = dis, b = r, c = p.dis();
double cos_a = (b * b + c * c - a * a) / 2.00 / b / c;
point cur = p / c * r;
return cur = cur.rotate(flag * acos(cos_a)), cur;
}
inline bool check(point st, int id, double max) {
int s = 0, t = 2 * n + 1;
for (int i = s; i <= t; ++i) edge[i].clear();
for (int i = 1; i < n; ++i) {
st = st.rotate(2.0 * PI / n);
int cc = 0;
for (int j = 1; j <= n; ++j)
if (j != id && (p[j] - st).dis2() <= max * max)
add_edge(j, i + n, 1), cc++;
if (cc == 0) return false;
}
for (int i = 1; i <= n; ++i) if (i != id) add_edge(s, i, 1);
for (int i = 1; i < n; ++i) add_edge(i + n, t, 1);
return (dinic(s, t) == n - 1);
}
inline bool check(double x) {
for (int i = 1; i <= n; ++i) {
if (x < p[i].dis() - r || x < r - p[i].dis()) return false;
if (x > p[i].dis() + r) {
st[i] = point(INF, INF);
continue ;
}
st[i] = get_pos(p[i], x, 1);
}
for (int i = 1; i <= n; ++i)
if (st[i].x != INF && check(st[i], i, x))
return true;
return false;
}
inline void solve() {
R(n), R(r);
for (int i = 1; i <= n; ++i) R(x), R(y), p[i] = point(x, y);
std::random_shuffle(p + 1, p + n + 1);
double l = -eps, r = 0;
for (int i = 1; i <= n; ++i) r = std::max(p[i].dis() + ::r, r);
int cnt = 0;
while (l + eps < r) {
double mid = (l + r) / 2;
check(mid) ? r = mid : l = mid;
}
printf("%0.8f", r);
}
int main() {
// freopen("in.in", "r", stdin);
// freopen("fleet.out", "w", stdout);
solve();
return 0;
}
考虑如何优化复杂度,我们发现如果把x轴正半轴上的(r, 0)当做其中一个坐标,那么所有的可行方案与这个方案的角度都不超过2pi / n,那么我们考虑维护逆时针进行偏转的过程,我们发现,如果一开始先将(r, 0)定位为1号点,那么随着偏转,每一个点原有的可行匹配点会在某一时刻减少一个,也会在某一时刻增加一个,那么我们可以将每一种操作的偏转角度记录下来,然后将操作排序,显然操作总个数是O(n)的,每一次操作相当于从网络流建图中删掉一条边,或者增加一条边,我们只需要将这个流量退掉,或者加上就可以了,因为是二分图,所以一次只会影响三条边,还是很轻松的,因为流量改变只有一,所以一次退流或者加边的复杂度都应该是O(n + m)的,那么总复杂度都是O(n3logn)。
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <deque>
#include <iterator>
#include <map>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
// /*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN], *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
fwrite(obuf, 1, oh - obuf, stdout);
}
/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
// */
const int MAXN = 200000 + 10;
const double PI = acos(-1.0);
const double eps = 1e-9;
const int INF = MAXN;
double block;
int s, t, n, r, x, y;
int dis[MAXN], temp[MAXN];
bool vis[MAXN];
struct point {
double x, y;
point() {}
point(double x, double y) : x(x), y(y) {}
inline double dis() {
return sqrt(x * x + y * y);
}
inline double dis2() {
return x * x + y * y;
}
} p[MAXN];
struct node {
int to, w, rev;
node() {}
node(int to, int w, int rev) : to(to), w(w), rev(rev) {}
} ;
std::vector<node> edge[MAXN];
struct data {
double num;
int u, v, type;
data() {}
data(double num, int u, int v, int type)
: num(num), u(u), v(v), type(type) {}
inline bool operator < (const data &a) const {
return (num == a.num) ? (type > a.type) : (num < a.num);
}
} q[MAXN << 1 | 1];
inline void add_edge(int x, int y, int w) {
edge[x].push_back(node(y, w, edge[y].size()));
edge[y].push_back(node(x, 0, edge[x].size() - 1));
}
inline bool bfs(int s, int t) {
static std::queue<int> q;
for (int i = s; i <= t; ++i) dis[i] = -1;
for (int i = s; i <= t; ++i) temp[i] = 0;
dis[s] = 0, q.push(s);
while (!q.empty()) {
int cur = q.front();
q.pop();
for (int p = 0; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (e->w > 0 && dis[e->to] == -1)
dis[e->to] = dis[cur] + 1, q.push(e->to);
}
}
return dis[t] != -1;
}
inline int dfs(int cur, int low, int t) {
if (cur == t) return low;
int delta = 0;
for (int &p = temp[cur]; p < edge[cur].size(); ++p) {
node *e = &edge[cur][p];
if (dis[e->to] == dis[cur] + 1 && e->w > 0) {
int ret = dfs(e->to, std::min(low - delta, e->w), t);
e->w -= ret, edge[e->to][e->rev].w += ret;
if ((delta += ret) == low) break ;
}
}
return delta;
}
int ret, ans;
inline void pop_flow(int u, int v) {
std::vector<node>::iterator it, it1;
bool flag;
for (it = edge[u].begin(); it != edge[u].end(); ++it)
if (it->to == v) {
flag = it->w, it = edge[u].erase(it);
break ;
}
for (; it != edge[u].end(); ++it) edge[it->to][it->rev].rev--;
for (it = edge[v].begin(); it != edge[v].end(); ++it)
if (it->to == u) {
it = edge[v].erase(it);
break ;
}
for (; it != edge[v].end(); ++it) edge[it->to][it->rev].rev--;
if (flag) return ;
ans--;
for (it = edge[s].begin(); it != edge[s].end(); ++it)
if (it->to == u) {
it->w ^= 1, edge[it->to][it->rev].w ^= 1;
break ;
}
for (it = edge[t].begin(); it != edge[t].end(); ++it)
if (it->to == v) {
it->w ^= 1, edge[it->to][it->rev].w ^= 1;
break ;
}
while (bfs(s, t))
do ret = dfs(s, INF, t);
while (ans += ret, ret);
}
inline bool check(double x) {
int cnt = 0;
for (int i = s; i <= t; ++i) edge[i].clear();
for (int i = 1; i <= n; ++i) {
double dis = p[i].dis();
if (dis > x + r || dis < r - x) return false;
if (dis + r <= x) for (int j = 1; j <= n; ++j) add_edge(i, j + n, 1);
else {
double angle1 = atan2(p[i].y, p[i].x);
double angle2 = acos(((double)r * r + dis * dis - x * x)
/ 2.0 / dis / r);
double al = angle1 - angle2, ar = angle1 + angle2;
while (al < 0) al += 2.0 * PI;
while (ar < 0) ar += 2.0 * PI;
int pl = al / block, pr = ar / block;
q[++cnt] = data(al - block * pl, i, pl + 1 + n, 1), pl++;
q[++cnt] = data(ar - block * pr, i, pr + 1 + n, 0), pr++;
if (al <= ar) for (int j = pl + 1; j <= pr; ++j)
add_edge(i, j + n, 1);
else {
for (int j = pl + 1; j <= n; ++j) add_edge(i, j + n, 1);
for (int j = 1; j <= pr; ++j) add_edge(i, j + n, 1);
}
}
}
for (int i = 1; i <= n; ++i) add_edge(s, i, 1), add_edge(i + n, t, 1);
std::sort(q + 1, q + cnt + 1), ret = ans = 0;
while (bfs(s, t))
do ret = dfs(s, INF, t);
while (ans += ret, ret);
if (ans == n) return true;
for (int i = 1; i <= cnt; ++i) {
if (q[i].type) {
add_edge(q[i].u, q[i].v, 1);
while (bfs(s, t))
do ret = dfs(s, INF, t);
while (ans += ret, ret);
if (ans == n) return true;
} else pop_flow(q[i].u, q[i].v);
}
return false;
}
inline void solve() {
R(n), R(r), block = PI * 2.00 / n, s = 0, t = 2 * n + 1;
for (int i = 1; i <= n; ++i) R(x), R(y), p[i] = point(x, y);
double l = -eps, r = 300;
while (l + eps < r) {
double mid = (l + r) / 2;
(check(mid)) ? r = mid : l = mid;
}
printf("%0.8f", r);
}
int main() {
freopen("in.in", "r", stdin);
solve();
return 0;
}