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;
}
posted @   Rainsheep  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示