jzoj3956. 【GDOI2015模拟12.20】鸡腿の梦境 (计算几何常见套路)

题意

有n+1个圆,其中一号圆保证不与其他圆重合,其他圆可能重合。
现在要你移动1号圆,在不相交的情况下(允许相切),问能不能逃出2~n+1号圆的包围圈。
n<=400

寄蒜几盒

之所以写下这题是因为涉及到一些trick
首先比较显然可以将1号圆变为点,其他圆半径加上一号圆半径。

然后将相交的圆的圆心间连上线段,现在的问题就变成是否存在一个多边形能将这个点包括在内。(只允许端点在圆心)

判断多边形在点内,常用的方法是射线法。
随机一个远点,引一条假装成射线的线段。 然后和每一条边判断是否有交点,考虑普通情况,无论凹凸,若交点个数是奇数,那么在该多边形中。
因为是随机,所以各种奇妙情况出现的概率是非常非常低的。
(数轴上选一个点,每个点都有概率,但都为0)

这样给每条边赋权,枚举起点开始bfs,若到一个点有奇偶两条路,则点在该多边形内。
(这里sb了一下,为什么是bfs呢? 因为若走到一个已走过的点,要么直接return 1,要么这个点不需要松弛)

然后比较懒,不想写向量全家福,因此可以用y=kx+b的斜截式。
只要将所有点旋转一个随机角度即可,也同上文出现特殊情况的概率是极小的。
(中了可以买彩票)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <ctime>
#define sqr(x) ((x) * (x))
using namespace std;
typedef double db;
const db pi = acos(-1), eps = 1e-8;
const int N = 310;
db x[N], y[N], r[N];
db qx,qy,ap,ox,oy,ok,ob;
int n;
db dis2(int a,int b) {
    return sqr(x[a] - x[b]) + sqr(y[a] - y[b]);
}
db rotate(db &x,db &y,db &ap) {
    db a=x,b=y;
    x = a * cos(ap) - b * sin(ap);
    y = a * sin(ap) + b * cos(ap);
}
int final[N],to[N*N],nex[N*N],e[N*N],tot;

void link(int x,int y,int w) {
    to[++tot]=y,nex[tot]=final[x],final[x]=tot;
    e[tot]=w;
}

void solve(db x,db y,db xx,db yy,db &k,db &b) {
    k = (yy-y) / (xx-x);
    b = y - k * x;
}

int jd(int i,int j) {
    db k1 = 0,b1 = 0;
    solve(x[i],y[i],x[j],y[j],k1,b1);
    db tx = (ob - b1) / (k1 - ok);
    return min(x[i],x[j]) <= tx && tx <= max(x[i],x[j])
        && min(qx,ox) <= tx && tx <= max(qx,ox);
}

int f[N],Q[N];
bool check(int i) {
    memset(f,0,sizeof f);
    f[i] = 2;
    int L = 0, R = 0;
    Q[++R] = i;
    while (L < R) {
        int x= Q[++L];
        for (int i=final[x]; i; i=nex[i]) {
            int y = to[i];
            int nz = (e[i] == 1) ? 3 - f[x] : f[x];
            if (f[y] == 0) {
                Q[++R] = y;
                f[y] = nz;
            } else {
                if (f[y] == nz) continue;
                return 1;
            }
        }
    }
    return 0;
}
int main() {
    srand(time(0)); rand();

    freopen("a.in","r",stdin);
    while (scanf("%d",&n) != EOF) {
        memset(final,0,sizeof final), tot = 0;

        ap = //pi / 4;
        pi * rand() / RAND_MAX;

        for (int i=1; i<=n; i++) {
            scanf("%lf %lf %lf",&x[i],&y[i],&r[i]);
            rotate(x[i],y[i],ap);
        }

        db mo = 0; cin>>qx>>qy>>mo;
        rotate(qx,qy,ap);
        ox =  rand();
        oy =  rand();

        for (int i=1; i<=n; i++) r[i]+=mo;
        solve(qx,qy,ox,oy,ok,ob);

        for (int i=1; i<=n; i++) {
            for (int j=i+1; j<=n; j++) {
                if (dis2(i,j) + eps < sqr(r[i] + r[j])) {
                    int w = jd(i,j);
                    link(i,j,w),link(j,i,w);
                }
            }
        }

        int flag = 0;
        for (int i=1; i<=n; i++) {
            if (check(i)) {
                printf("NO\n");
                flag = 1;
                break;
            }
        }
        if (!flag) printf("YES\n");
    }
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/80991024