BZOJ 1822: [JSOI2010]Frozen Nova 冷冻波
二分时间转化为判定性问题
考虑网络流
\(S\) 向第 \(i\) 个巫妖连一条容量为 \(\dfrac{mid}{time_i}+1\) 的边
第 \(j\) 个精灵向 \(T\) 连一条容量为 \(1\) 的边
预处理出每个巫妖能否攻击每个精灵
通过判断圆和线段是否有交点即可
判断方法为圆心找到离线段的最近距离和半径比较
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define pii pair<int, int>
#define pli pair<ll, int>
#define SZ(x) ((int)(x).size())
#define lp p << 1
#define rp p << 1 | 1
#define mid ((l + r) / 2)
#define lowbit(i) ((i) & (-i))
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=((b)-1);i>=(a);i--)
#define Edg int ccnt=1,head[N],to[E],ne[E];void addd(int u,int v){to[++ccnt]=v;ne[ccnt]=head[u];head[u]=ccnt;}void add(int u,int v){addd(u,v);addd(v,u);}
#define Edgc int ccnt=1,head[N],to[E],ne[E],c[E];void addd(int u,int v,int w){to[++ccnt]=v;ne[ccnt]=head[u];c[ccnt]=w;head[u]=ccnt;}void add(int u,int v,int w){addd(u,v,w);addd(v,u,w);}
#define es(u,i,v) for(int i=head[u],v=to[i];i;i=ne[i],v=to[i])
const int MOD = 1000000007,INF=0x3f3f3f3f;
const ll inf=0x3f3f3f3f3f3f3f3f;
void M(int &x) {if (x >= MOD)x -= MOD; if (x < 0)x += MOD;}
int qp(int a, int b = MOD - 2) {int ans = 1; for (; b; a = 1LL * a * a % MOD, b >>= 1)if (b & 1)ans = 1LL * ans * a % MOD; return ans % MOD;}
template<class T>T gcd(T a, T b) { while (b) { a %= b; std::swap(a, b); } return a; }
template<class T>bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<class T>bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
const db eps = 1e-9;
inline int sign(db k) { return k < -eps ? -1 : k > eps; }
inline int cmp(db k1, db k2) { return sign(k1 - k2); }
struct P {
db x, y;
P() {}
P(db x, db y): x(x), y(y) {}
P operator + (const P &p) const { return {x + p.x, y + p.y}; }
P operator - (const P &p) const { return {x - p.x, y - p.y}; }
P operator * (db k) const { return {x * k, y * k}; }
P operator / (db k) const { return {x / k, y / k}; }
bool operator < (const P &p) const {
int c = cmp(x, p.x);
return c ? c == -1 : cmp(y, p.y) == -1;
}
bool operator == (const P &p) const {
return !cmp(x, p.x) && !cmp(y, p.y);
}
db distTo(const P &p) const { return (*this - p).abs(); }
db alpha() { return atan2(y, x); }
void read() { scanf("%lf%lf", &x, &y); }
void print() { printf("%.10f %.10f\n", x, y); }
db abs() { return sqrt(abs2()); }
db abs2() { return x * x + y * y; }
P rot(const db &k) { return P(x * cos(k) - y * sin(k), x * sin(k) + y * cos(k)); }
P rot90() { return P(-y, x); }
P unit() { return *this / abs(); }
P normal() { return rot90() / abs(); }
int quad() const { return sign(y) == 1 || (sign(y) == 0 && sign(x) >= 0); }
P getdel() {if (sign(x) == -1 || (sign(x) == 0 && sign(y) == -1)) return (*this) * (-1); else return (*this);}
db dot(const P &p) { return x * p.x + y * p.y; }
db det(const P &p) { return x * p.y - y * p.x; }
};
// 点在线段上判定
bool isMiddle(db a, db m, db b) {
return sign(a - m) == 0 || sign(b - m) == 0 || (a < m != b < m);
}
bool isMiddle(const P &a, const P &m, const P &b) {
return isMiddle(a.x, m.x, b.x) && isMiddle(a.y, m.y, b.y);
}
// 投影
P proj(const P &p1, const P &p2, const P &q) {
P dir = p2 - p1;
return p1 + dir * (dir.dot(q - p1) / dir.abs2());
}
// 最近点
db nearest(const P &p1, const P &p2, const P &q) {
P h = proj(p1, p2, q);
if (isMiddle(p1, h, p2)) return q.distTo(h);
return std::min(p1.distTo(q), p2.distTo(q));
}
struct Circle {
P o; db r;
void read() { o.read(); scanf("%lf", &r); }
bool cut(const P &p1, const P &p2) {
return cmp(r, nearest(p1, p2, o)) >= 0;
}
};
const int N = 444;
P a[N], b[N];
Circle cir[N];
int n, m, k;
int maxdis[N], tim[N], mp[N][N];
bool vis[N];
namespace Dinic {
int head[N], cnt = 1, iter[N], level[N];
struct E {
int v, ne, f;
} e[N * N + 2];
inline void add(int u, int v, int f) {
e[++cnt].v = v; e[cnt].ne = head[u]; e[cnt].f = f; head[u] = cnt;
std::swap(u, v);
e[++cnt].v = v; e[cnt].ne = head[u]; e[cnt].f = 0; head[u] = cnt;
}
bool bfs(int s, int t) {
for (int i = 0; i <= t; i++) level[i] = -1, iter[i] = head[i];
std::queue<int> que;
que.push(s);
level[s] = 0;
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = head[u]; i; i = e[i].ne) {
int v = e[i].v, f = e[i].f;
if (level[v] < 0 && f) {
level[v] = level[u] + 1;
que.push(v);
}
}
}
return level[t] != -1;
}
int dfs(int u, int t, int f) {
if (u == t || !f) return f;
int flow = 0;
for (int i = iter[u]; i; i = e[i].ne) {
iter[u] = i;
int v = e[i].v;
if (level[v] == level[u] + 1 && e[i].f) {
int w = dfs(v, t, std::min(f, e[i].f));
if (!w) continue;
e[i].f -= w, e[i ^ 1].f += w;
flow += w, f -= w;
if (f <= 0) break;
}
}
return flow;
}
int Maxflow(int s, int t) {
int ans = 0;
while (bfs(s, t)) ans += dfs(s, t, INF);
return ans;
}
bool check(int cur) {
int s = 0, t = n + m + 1;
rep (i, 0, t + 1) head[i] = 0;
cnt = 1;
rep (i, 1, n + 1)
add(s, i, cur / tim[i] + 1);
rep (i, 1, n + 1)
rep (j, 1, m + 1) {
if (mp[i][j]) add(i, n + j, 1);
}
rep (i, 1, m + 1) add(n + i, t, 1);
return Maxflow(s, t) == m;
}
}
int main() {
#ifdef LOCAL
freopen("ans.out", "w", stdout);
#endif
int r = 0;
scanf("%d%d%d", &n, &m, &k);
rep (i, 1, n + 1) a[i].read(), scanf("%d%d", maxdis + i, tim + i), chkmax(r, tim[i]);
rep (i, 1, m + 1) b[i].read();
rep (i, 1, k + 1) cir[i].read();
rep (i, 1, n + 1) {
rep (j, 1, m + 1) {
if (cmp(a[i].distTo(b[j]), maxdis[i]) > 0) continue;
int flag = 1;
rep (z, 1, k + 1) {
if (cir[z].cut(a[i], b[j])) {
flag = 0;
break;
}
}
if (flag) mp[i][j] = 1, vis[j] = 1;
}
}
rep (i, 1, m + 1) if (!vis[i]) { puts("-1"); return 0; }
int l = 0, ans = 0; r *= m;
while (l <= r) {
if (Dinic::check(mid)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
#ifdef LOCAL
printf("%.10f\n", (db)clock() / CLOCKS_PER_SEC);
#endif
return 0;
}