[BalticOI 2016 day1]Park(并查集+计算几何)

[BalticOI 2016 day1]Park(并查集+计算几何)

题面

在 Byteland 的首都,有一个以围墙包裹的矩形公园,其中以圆形表示游客和树。
公园里有四个入口,分别在四个角落(1, 2, 3, 4)分别对应左下、右下、左上、右上)。游客只能从入口进出。
游客可以在他们与公园的两邻边相切的时候进出对应的出口。游客可以在公园里自由活动但不允许与树重叠。
给出\(n\)棵树和\(m\)个游客,你的任务是为每个游客计算,给定他们进入公园的入口,他们可以从哪个入口离开公园。

\(n \leq 2000,m \leq 10^5\)

分析

我们反过来考虑,求出每两个口间可以通过的圆的半径的最大值。

把每棵树和公园的4个边界看成点,把任意两者间可以通过的半径作为边权建图。如果图上存在一条连接两个边界的路径,使得路径上边权都$\leq $游客的半径,那么游客就不能通过。具体是哪些边界取决于游客的起点和终点。

那么我们将询问离线,并按照半径从小到大排序.同时也将边权从小到大排序。将小于当前半径\(\times 2\)的边加入,那么就可以根据上面的方法判断是否联通。对于每一对出入口分类讨论,建议自己画图观察。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 4000000
#define maxm 100000
using namespace std;
typedef long long db;
int n,m;
db W,H;
struct circle{
    db x;
    db y;
    db r;
}c[maxn+5];
struct edge{
    int from;
    int to;
    db len;
    friend bool operator < (edge p,edge q){
        return p.len<q.len;
    }
}E[maxn+5];
int esz=1;
void add_edge(int u,int v,db w){
    esz++;
    E[esz].from=u,E[esz].to=v,E[esz].len=w;
}

struct qry{
    int id;
    int pos;
    db r;
    friend bool operator < (qry p,qry q){
        return p.r<q.r;
    }
}q[maxm+5];
inline db dist(db x1,db y1,db x2,db y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

struct DSU{
    int fa[maxn+5];
    int find(int x){
        if(fa[x]==x) return x;
        else return fa[x]=find(fa[x]);
    }
    inline void merge(int x,int y){
        fa[find(x)]=find(y);
    }
    inline void ini(int n){
        for(int i=1;i<=n;i++) fa[i]=i;
    }
}S;

inline bool ok(int x,int y){
    //不能通过,即两个边界对应的点被中间长度>2r边连通 
    //能通过反之 
    return S.find(n+x)!=S.find(n+y);
}
int ans[maxn+5][5];
int main(){
#ifndef LOCAL
    freopen("corner.in","r",stdin);
    freopen("corner.out","w",stdout);
#endif
    scanf("%d %d %lld %lld",&n,&m,&W,&H);
    for(int i=1;i<=n;i++) scanf("%lld %lld %lld",&c[i].x,&c[i].y,&c[i].r);
    for(int i=1;i<=m;i++){
        scanf("%lld %d",&q[i].r,&q[i].pos);
        q[i].id=i;
    }
    for(int i=1;i<=n;i++){
        for(int j=i+1;j<=n;j++){
            add_edge(i,j,dist(c[i].x,c[i].y,c[j].x,c[j].y)-c[i].r-c[j].r);
        }
        add_edge(i,n+1,c[i].x-c[i].r);//n+1,n+2,n+3,n+4 ->左,下,右,上 
        add_edge(i,n+2,c[i].y-c[i].r);
        add_edge(i,n+3,W-c[i].x-c[i].r);
        add_edge(i,n+4,H-c[i].y-c[i].r);
    }
    S.ini(n+4);
    sort(E+1,E+1+esz);
    sort(q+1,q+1+m);
    int ptr=0;
    for(int i=1;i<=m;i++){
        while(ptr<esz&&E[ptr+1].len<q[i].r*2){
            ptr++;
            S.merge(E[ptr].from,E[ptr].to);
        }
        int id=q[i].id,pos=q[i].pos;
        ans[id][pos]=1;
        if(pos==1){
            if(ok(1,2)&&ok(2,4)&&ok(2,3)) ans[id][2]=1;
            if(ok(1,2)&&ok(2,4)&&ok(1,3)&&ok(3,4)) ans[id][3]=1;
            if(ok(1,2)&&ok(1,3)&&ok(1,4)) ans[id][4]=1;
        }else if(pos==2){
            if(ok(1,2)&&ok(2,3)&&ok(2,4)) ans[id][1]=1;
            if(ok(2,3)&&ok(1,3)&&ok(3,4)) ans[id][3]=1;
            if(ok(2,3)&&ok(1,3)&&ok(2,4)&&ok(1,4)) ans[id][4]=1;
        }else if(pos==3){
            if(ok(3,4)&&ok(1,3)&&ok(2,4)&&ok(1,2)) ans[id][1]=1;
            if(ok(3,4)&&ok(1,3)&&ok(2,3)) ans[id][2]=1;
            if(ok(3,4)&&ok(2,4)&&ok(1,4)) ans[id][4]=1;
        }else{
            if(ok(1,4)&&ok(1,3)&&ok(1,2)) ans[id][1]=1;
            if(ok(1,4)&&ok(2,4)&&ok(1,3)&&ok(2,3)) ans[id][2]=1;
            if(ok(1,4)&&ok(2,4)&&ok(3,4)) ans[id][3]=1;
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=4;j++) if(ans[i][j]) printf("%d",j);
        printf("\n");
    }
}
posted @ 2020-11-30 15:29  birchtree  阅读(142)  评论(0编辑  收藏  举报