P4518 [JSOI2018] 绝地反击 题解
大家好啊,因为我做题做崩了所以来补题解了,提供一种模拟网络流的 \(O(n^3\log n)\) 的做法。
首先二分答案,那么每个飞船能到达的区域是一个圆,将这个圆与攻击轨道取交就出现了一段圆弧,不妨视作匹配,那么每个点就能匹配一段圆弧上的一段点。
关于这个圆弧以及两个交点的极角怎么算,可以参考 P2510。
一个引理,最佳方案中必然存在一种方案,使得该方案中存在一点卡在其所交圆弧的端点。这个易证,不然你就把这个多边形转一转就能让某个点卡到端点。
枚举 \(2n\) 个点(每段圆弧的两个端点)作为被卡住的点,顺次得到另外 \(n - 1\) 个点作为多边形的端点,原问题转化为二分图是否存在完美匹配。
Hall 定理的做法可以参考别的题解,这里讲讲怎么模拟网络流。
首先不妨将卡住的点视作正多边形旋转了一个角度,因为多边形的顶点之间相互没有区别,所以将所有的角度对 \(\dfrac{2\pi}{n}\) 取模并排序,然后考虑每转过一个角度的影响。
特判相离和覆盖的情况,然后考虑每个角度只会添加/删除 \(O(1)\) 条边,所以可以模拟推流/退流。
具体来说,推流就是直接加边之后再增广一次,删边也不难,考虑路径 \(s \to u \to v \to t\),将 \((u, v)\) 的流量设为 \(0\),再反转 \((s, u)\) 和 \((v, t)\) 两条边的流量即可。
代码
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
// #define int LL
const double PI = acos(-1);
const double eps = 1e-8;
const int maxn = 410;
namespace netflow {
int tot = -1, h[maxn];
int mf = 0;
struct E { int v, nx, f; } e[maxn * maxn << 1];
inline void add(int u, int v, int f) {
if(v == -2147483644) exit(0);
e[++ tot] = {v, h[u], f}, h[u] = tot;
e[++ tot] = {u, h[v], 0}, h[v] = tot;
}
int s, t, c[maxn], d[maxn];
const int INF = 1e9;
inline int find(int u, int l) {
if(u == t) return l;
int flow = 0;
for(int i = c[u]; ~i && flow < l; i = e[i].nx) {
c[u] = i;
auto [v, nx, fw] = e[i];
if(fw && d[v] == d[u] + 1) {
auto f = find(v, min(l - flow, fw));
if(!f) d[v] = -1;
e[i].f -= f, e[i ^ 1].f += f, flow += f;
}
} return flow;
}
int q[maxn];
int hh, tt;
inline bool bfs() {
q[hh = tt = 0] = s, c[s] = h[s];
memset(d, -1, sizeof d), d[s] = 0;
while(hh <= tt) {
int u = q[hh ++ ];
for(int i = h[u]; ~i; i = e[i].nx) {
auto [v, nx, fw] = e[i];
if(fw && d[v] == -1) {
d[v] = d[u] + 1;
c[v] = h[v];
if(v == t) return 1;
q[++ tt] = v;
}
}
} return 0;
}
inline void Dinic() {
int r = 0;
while(bfs()) while(r = find(s, INF)) mf += r;
}
}
using namespace netflow;
int n, R;
struct vec {
double x, y;
inline int operator & (const vec& t) const { return x * t.x + y * t.y; }
inline int operator * (const vec& t) const { return x * t.y - y * t.x; }
inline vec operator - (const vec& t) const { return {x - t.x, y - t.y}; }
} a[maxn], O;
inline double ang(vec v) { return atan2(v.y, v.x); }
inline double sqr(double x) { return x * x; }
inline double len(vec v) { return sqrt(v & v); }
inline double dist(vec a, vec b) { return len(b - a); }
inline int mod(double& x, double y) {
int t = x / y;
x -= t * y; return t + 1;
}
double D;
struct rad {
double ra;
int u, v;
int oper;
bool operator < (const rad& t) const { return ra == t.ra ? oper > t.oper : ra < t.ra; }
} b[maxn];
inline void del(int u, int v) {
bool run = 0;
for(int i = h[u]; ~i; i = e[i].nx)
if(e[i].v == v) { run = e[i ^ 1].f, e[i].f = e[i ^ 1].f = 0; break ; }
if(!run) return ;
for(int i = h[s]; ~i; i = e[i].nx)
if(e[i].v == u) { swap(e[i].f, e[i ^ 1].f); break ; }
for(int i = h[v]; ~i; i = e[i].nx)
if(e[i].v == t) { swap(e[i].f, e[i ^ 1].f); break ; }
mf -- ;
Dinic();
}
inline bool check(double r) {
tot = -1, mf = 0;
memset(h, -1, sizeof h);
for(int i = 1; i <= n; ++ i) { // 处理 2n 个交点
double le = dist(O, a[i]);
if(le > r + R || R > le + r) return 0;
if(R + le <= r) {
for(int j = 1; j <= n; ++ j) add(i, j + n, 1);
continue ;
}
// double th1 = acos((sqr(r) + sqr(le) - sqr(R)) / (2 * le * r));
double th1 = acos((-sqr(r) + sqr(le) + sqr(R)) / (2 * le * R));
double th2 = atan2(a[i].y, a[i].x);
double thl = th2 - th1, thr = th2 + th1;
while(thl < 0) thl += 2 * PI;
while(thr < 0) thr += 2 * PI;
int l = mod(thl, D), r = mod(thr, D);
if(l <= r) for(int j = l + 1; j <= r; ++ j) add(i, j + n, 1);
else {
for(int j = 1; j <= r; ++ j) add(i, j + n, 1);
for(int j = l + 1; j <= n; ++ j) add(i, j + n, 1);
}
b[(i << 1) - 1] = {thl, i, l, 1};
b[i << 1] = {thr, i, r, -1};
}
for(int i = 1; i <= n; ++ i) add(s, i, 1), add(i + n, t, 1);
sort(b + 1, b + (n << 1 | 1));
Dinic();
if(mf == n) return 1;
for(int i = 1; i <= (n << 1); ++ i) {
auto [ra, u, v, oper] = b[i];
if(~oper) {
add(u, v + n, 1);
Dinic();
if(mf == n) return 1;
} else {
del(u, v + n);
}
} return 0;
}
signed main() {
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
O.x = 0., O.y = 0.;
cin >> n >> R;
netflow::t = n << 1 | 1;
D = 2 * PI / n;
for(int i = 1; i <= n; ++ i) cin >> a[i].x >> a[i].y;
double l = 0, r = 400;
while(r - l > eps) {
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
} cout << fixed << setprecision(8) << l << '\n';
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!