[并查集] BZOJ 5183: [Baltic2016]Park

本文介绍了一种路径查找算法,用于解决给定平面上多个圆形障碍物和人员如何避免障碍物到达指定出口的问题。通过预处理圆之间的最短距离,并结合并查集的方法来维护圆与边界之间的连通性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DescriptionDescription

给定W×HW×H的平面。四个角落分别是四个出入口。
nn个圆,用三元组(x,y,r)表示。
mm个人,用二元组(r,e)表示。
求每个人不与任何圆相交可以到达哪些出入口。

SolutionSolution

考虑离线。
O(n2)O(n2)个每两个圆之间的最短距离排序。
mm个人的直径排序。
n个圆和上下左右四个边界看成节点。
考虑把圆按顺序加入图内,用并查集维护连通性。
分类讨论每两个边界联通后会导致四个角落连通性的变化。

using namespace std;

const double eps = 1e-4;
const int N = 2020;
const int M = 101010;
typedef long long ll;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0;
    for (c = get(); c < '0' || c > '9'; c = get());
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}

struct Point {
    ll x, y, r;
    inline double operator -(const Point &a) {
        return (sqrt((x - a.x) * (x - a.x) + (y - a.y) * (y - a.y))) - r - a.r;
    }
};
struct cpp {
    ll x, id, r;
    inline bool operator <(const cpp &a) const{
        return r < a.r;
    }
};
struct PPairs {
    ll x, y;
    double d;
    inline bool operator <(const PPairs &a) const {
        return d < a.d;
    }
    PPairs(double _d = 0, ll _x = 0, ll _y = 0):x(_x), y(_y), d(_d) {}
};
Point p[N];
cpp a[M];
PPairs d[N * N];
int n, m, w, h, p1, dnt;
int fa[N], rk[N];
int ans[M][5];
int mp[5][5];

inline int Fa(int x) {
    return x == fa[x] ? x : fa[x] = Fa(fa[x]);
}
inline int Merge(int x, int y) {
    static int f1, f2;
    f1 = Fa(x); f2 = Fa(y);
    if (f1 == f2) return false;
    if (rk[f1] > rk[f2]) swap(f1, f2);
    if (rk[f1] == rk[f2]) ++rk[f2];
    fa[f1] = f2; return true;
}
inline void Link(int x, int y) {
    mp[x][y] = mp[y][x] = 0;
}

// n + 1 up, n + 2 down, n + 3 left and n + 4 right
// 1 = bottom-left, 2 = bottom-right, 3 = top-right, 4 = top-left

int main(void) {
    freopen("park.in", "r", stdin);
    freopen("park.out", "w", stdout);
    read(n); read(m);
    read(w); read(h);
    for (int i = 1; i <= n; i++) {
        read(p[i].x); read(p[i].y); read(p[i].r);
    }
    for (int i = 1; i <= m; i++) {
        read(a[i].r); read(a[i].x);
        a[i].r *= 2; a[i].id = i;
    }
    for (int i = 1; i <= n; i++) {
        d[++dnt] = PPairs(h - p[i].y - p[i].r, i, n + 1);
        d[++dnt] = PPairs(p[i].y - p[i].r, i, n + 2);
        d[++dnt] = PPairs(p[i].x - p[i].r, i, n + 3);
        d[++dnt] = PPairs(w - p[i].x - p[i].r, i, n + 4);
        for (int j = i + 1; j <= n; j++)
            d[++dnt] = PPairs(fabs(p[j] - p[i]), i, j);
    }
    sort(d + 1, d + dnt + 1);
    sort(a + 1, a + m + 1);
    for (int i = 1; i <= n + 4; i++) fa[i] = i;
    p1 = 1;
    for (int i = 1; i <= 4; i++)
        for (int j = 1; j <= 4; j++)
            mp[i][j] = 1;
    for (int i = 1; i <= m; i++) {
        while (d[p1].d + eps < a[i].r && p1 <= dnt) {
            Merge(d[p1].x, d[p1].y); ++p1;
        }
        if (Fa(n + 1) == Fa(n + 2)) {
            Link(1, 2); Link(1, 3);
            Link(4, 2); Link(4, 3);
        }
        if (Fa(n + 1) == Fa(n + 3)) {
            Link(4, 1); Link(4, 2);
            Link(4, 3);
        }
        if (Fa(n + 1) == Fa(n + 4)) {
            Link(3, 1); Link(3, 2);
            Link(3, 4);
        }
        if (Fa(n + 2) == Fa(n + 3)) {
            Link(1, 2); Link(1, 3);
            Link(1, 4);
        }
        if (Fa(n + 2) == Fa(n + 4)) {
            Link(2, 1); Link(2, 3);
            Link(2, 4);
        }
        if (Fa(n + 3) == Fa(n + 4)) {
            Link(1, 3); Link(1, 4);
            Link(2, 3); Link(2, 4);
        }
        for (int j = 1; j <= 4; j++)
            ans[a[i].id][j] = mp[a[i].x][j];
    }
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= 4; j++)
            if (ans[i][j]) putchar('0' + j);
        putchar('\n');
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值