JZOJ3956. 【GDOI2015模拟12.20】鸡腿の梦境

题意

【故事の背景】

鸡腿是CZYZ的著名DS,他为了树立高富帅的伟大形象决定暑假去张江大学学习(游玩)。去的第一天晚上因为蚊子很多,鸡腿不堪其扰怒而打了一夜游戏。 第二天鸡腿吸取教训,弄好了蚊帐,自然是睡了一个好觉。鸡腿似乎还记得做的那个梦……

【问题の描述】

鸡腿做了一个好梦呢!他化身钢铁侠大战全宇宙的各种怪物,并且取得了胜利。在梦的结尾,鸡腿开的飞船困在了一片废弃飞船群中,同时由于引擎故障,他只能在平面内移动。废弃飞船群可以描述为许多圆形的飞船,当然因为各种奇怪的原因,飞船可能叠加在一切。鸡腿也驾驶着飞船在这个平面中。一开始的时候鸡腿的飞船是不会和废弃的飞船有叠加部分的,鸡腿驶出这片区域时,可以蹭过某一个废弃飞船,但不能撞上更不能挤开它(质量问题),更不能和废弃飞船叠起来。鸡腿要立刻离开这片区域进行补给,你能告诉他现有条件下可以离开这片废弃飞船群吗?

数据范围

对于30%的数据N ≤ 15;
对于40%的数据 N ≤ 150;
对于 100%的数据 N ≤ 300,数据组数 ≤ 5。

Analysis

计算几何,理所当然的GG了。首先考虑起点是一个圆,不好做。把给定圆的半径加上起点圆的半径,此时将问题转化为对起点为一个点的情况解决。此时对于相交的圆连边,任意引一条从起点射出的射线,与其相交的边权值设为 1 1 <script type="math/tex" id="MathJax-Element-42">1</script>。在建好的图中,若有权值为奇数的环,则起点出不去。因为若是偶数,代表起点可以绕过这个环走出去。

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
# include<cmath>
using namespace std;
const int N = 3e2 + 5;
typedef double db;
struct node
{
    db x,y,r;
}p[N],fi;
int f[N][2],st[N],to[N * N * 2],nx[N * N * 2],len[N * N * 2];
int n,tot;
db calc(node x,node y)
{ return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y)); }
void add(int u,int v,int w)
{
    to[++tot] = v,nx[tot] = st[u],len[tot] = w,st[u] = tot;
    to[++tot] = u,nx[tot] = st[v],len[tot] = w,st[v] = tot;
}
bool pd(node a,node b,node c,node d)
{
    db u,v,w,z;
    u = (c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y);
    v = (d.x - a.x) * (b.y - a.y) - (b.x - a.x) * (d.y - a.y);
    w = (a.x - c.x) * (d.y - c.y) - (d.x - c.x) * (a.y - c.y);
    z = (b.x - c.x) * (d.y - c.y) - (d.x - c.x) * (b.y - c.y);
    return (u * v <= 0.000001 && w * z <= 0.000001);
}
void dfs(int x,int t,int fr)
{
    if (f[x][t]) return;
    f[x][t] = 1;
    for (int i = st[x] ; i ; i = nx[i])
        if (to[i] != fr) dfs(to[i],t ^ len[i],x);
}
int main()
{
    node ed = (node){9024.0,6002.0,0.0};
    while (~scanf("%d",&n))
    {
        memset(st,0,sizeof(st)); tot = 0;
        for (int i = 1 ; i <= n ; ++i) scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
        scanf("%lf%lf%lf",&fi.x,&fi.y,&fi.r);
        for (int i = 1 ; i <= n ; ++i) p[i].r += fi.r;
        for (int i = 1 ; i < n ; ++i)
            for (int j = i + 1 ; j <= n ; ++j)
            {
                if (calc(p[i],p[j]) < p[i].r + p[j].r)
                {
                    if (pd(p[i],p[j],fi,ed)) add(i,j,1);
                    else add(i,j,0);
                }
            }
        bool flag = 1;
        for (int i = 1 ; i <= n ; ++i)
        {
            memset(f,0,sizeof(f));
            dfs(i,0,0);
            for (int j = 1 ; j <= n ; ++j)
            if (f[j][0] && f[j][1]) { flag = 0; break; }
            if (!flag) break;
        }
        if (flag) puts("YES");
        else puts("NO");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值