[BZOJ3199][Sdoi2013]escape(半平面交+bfs)

一个直接的想法:能否求出每个亲戚的监视范围(也就是说亲戚 i i 的监视范围内任意一个点到 i 的距离都是最短的)。
考虑一个点 (x,y) ( x , y ) ,它到亲戚 i i 的距离比到亲戚 j 短的条件:
做线段 ij i j 的垂直平分线 l l ,那么 (x,y) 到亲戚 i i 的距离比到亲戚 j 短的条件就是 (x,y) ( x , y ) 直线 l l 朝向点 i 的一侧的半平面内
而求第 i i 个亲戚的监视范围,就是对于所有的 ji ,做线段 ij i j 的垂直平分线,把这些直线放在一起求一遍半平面交(注意要加上矩形边界的 4 4 条直线)。
然后对于一对 i,j ,如果 i i j 的监视范围有公共边,则连边 (i,j) ( i , j )
对于任何一个 i i ,如果 i 的监视范围在矩形的边界上,则连边 (i,n+1) ( i , n + 1 )
n+1 n + 1 是一个虚拟节点,表示矩形外的部分)
然后找出起始点所在的区域,用 BFS 求出最短路即可解决。
代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Edge(u) for (int e = adj[u], v; e; e = nxt[e])
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 625, M = 4e6 + 5;
int n, m, xl, yl, X0, Y0, ecnt, nxt[M], adj[N], go[M], S, H,
T, len, que[N], dis[N];
bool vis[N], conn[N][N];
void add_edge(int u, int v) {
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
struct cyx {
    double x, y; cyx() {} cyx(double _x, double _y) : x(_x), y(_y) {}
    friend inline cyx operator + (cyx a, cyx b) {
        return cyx(a.x + b.x, a.y + b.y);
    }
    friend inline cyx operator - (cyx a, cyx b) {
        return cyx(b.x - a.x, b.y - a.y);
    }
    friend inline cyx operator * (cyx a, double b) {
        return cyx(a.x * b, a.y * b);
    }
    friend inline cyx operator / (cyx a, double b) {
        return cyx(a.x / b, a.y / b);
    }
    friend inline double operator * (cyx a, cyx b) {
        return a.x * b.y - a.y * b.x;
    }
} otz[N];
double dist(cyx a, cyx b) {
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
struct pyz {
    int id; cyx a, b; pyz() {} pyz(cyx x, cyx y, int i) : a(x), b(y), id(i) {}
    cyx o() {return a - b;}
} a[N], b[N], Q[N];
cyx orz(pyz x, pyz y) {
    double s1 = (x.a - y.a) * (x.a - y.b), s2 = (x.b - y.b) * (x.b - y.a);
    return x.a + x.o() * s1 / (s1 + s2);
}
bool check1(pyz x, pyz y) {
    double tmp = x.o() * y.o();
    return tmp > 1e-8 || (fabs(tmp) <= 1e-8 && y.o() * (y.a - x.a) > 1e-8);
}
bool check2(pyz a, pyz b, pyz x) {
    cyx y = orz(a, b); return fabs((x.a - y) * x.o()) <= 1e-8
        || (x.a - y) * x.o() > 1e-8;
}
bool para(pyz x, pyz y) {
    cyx a = x.o(), b = y.o(), c = cyx(0, 0);
    return a * b == 0 && fabs(dist(a, c) + dist(c, b) - dist(a, b)) > 1e-8;
}
bool comp(pyz x, pyz y) {
    cyx p = x.o(), q = y.o();
    if ((p.y < 0 || (p.y == 0 && p.x < 0))
        && (q.y > 0 || (q.y == 0 && q.x > 0))) return 1;
    if ((p.y > 0 || (p.y == 0 && p.x > 0))
        && (q.y < 0 || (q.y == 0 && q.x < 0))) return 0;
    return check1(x, y);
}
void solvecyx() {
    int i, n = 0; sort(b + 1, b + m + 1, comp);
    For (i, 1, m) if (i == 1 || !para(b[i], b[i - 1])) a[++n] = b[i];
    Q[H = 1] = a[1]; Q[T = 2] = a[2]; For (i, 3, n) {
        while (H < T && check2(Q[T - 1], Q[T], a[i])) T--;
        while (H < T && check2(Q[H], Q[H + 1], a[i])) H++;
        Q[++T] = a[i];
    }
    while (H < T && check2(Q[T - 1], Q[T], Q[H])) T--;
}
pyz midpyz(cyx a, cyx b, int id) {
    cyx st = cyx((a.x + b.x) / 2, (a.y + b.y) / 2), delta = st - a;
    delta = cyx(delta.y, -delta.x); return pyz(st, st + delta, id);
}
void getex(int x) {
    int i; m = 0; For (i, 1, n) if (x != i) b[++m] = midpyz(otz[x], otz[i], i);
    b[++m] = pyz(cyx(0, 0), cyx(xl, 0), -1);
    b[++m] = pyz(cyx(xl, 0), cyx(xl, yl), -1);
    b[++m] = pyz(cyx(xl, yl), cyx(0, yl), -1);
    b[++m] = pyz(cyx(0, yl), cyx(0, 0), -1); solvecyx();
    For (i, H, T) if (Q[i].id == -1) add_edge(x, n + 1);
        else conn[x][Q[i].id] = 1; bool flag = 1; For (i, H, T)
        if (fabs((Q[i].a - cyx(X0, Y0)) * Q[i].o()) <= 1e-8
            || (Q[i].a - cyx(X0, Y0)) * Q[i].o() > 1e-8) flag = 0;
    if (flag) S = x;
}
int bfs() {
    int i; dis[que[len = 1] = S] = 0; vis[S] = 1; For (i, 1, len) {
        int u = que[i]; Edge(u) {
            if (vis[v = go[e]]) continue; vis[v] = 1;
            dis[que[++len] = v] = dis[u] + 1;
        }
    }
    return dis[n + 1];
}
void work() {
    int i, j; n = read(); xl = read(); yl = read(); X0 = read(); Y0 = read();
    For (i, 1, n) otz[i].x = read(), otz[i].y = read(); S = n + 1;
    if (X0 <= 0 || Y0 <= 0 || X0 >= xl || Y0 >= yl) return (void) puts("0");
    ecnt = 0; memset(adj, 0, sizeof(adj)); memset(vis, 0, sizeof(vis));
    memset(conn, 0, sizeof(conn)); For (i, 1, n) getex(i);
    For (i, 1, n) For (j, i + 1, n) if (conn[i][j] && conn[j][i])
        add_edge(i, j); printf("%d\n", bfs());
}
int main() {
    int T = read(); while (T--) work();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值