JZOJ 3956. 【GDOI2015模拟12.20】鸡腿の梦境
题目
Description
【故事の背景】
鸡腿是CZYZ的著名DS,他为了树立高富帅的伟大形象决定暑假去张江大学学习(游玩)。去的第一天晚上因为蚊子很多,鸡腿不堪其扰怒而打了一夜游戏。 第二天鸡腿吸取教训,弄好了蚊帐,自然是睡了一个好觉。鸡腿似乎还记得做的那个梦……
【问题の描述】
鸡腿做了一个好梦呢!他化身钢铁侠大战全宇宙的各种怪物,并且取得了胜利。在梦的结尾,鸡腿开的飞船困在了一片废弃飞船群中,同时由于引擎故障,他只能在平面内移动。废弃飞船群可以描述为许多圆形的飞船,当然因为各种奇怪的原因,飞船可能叠加在一切。鸡腿也驾驶着飞船在这个平面中。一开始的时候鸡腿的飞船是不会和废弃的飞船有叠加部分的,鸡腿驶出这片区域时,可以蹭过某一个废弃飞船,但不能撞上更不能挤开它(质量问题),更不能和废弃飞船叠起来。鸡腿要立刻离开这片区域进行补给,你能告诉他现有条件下可以离开这片废弃飞船群吗?
Input
有多组数据,文件以EOF结束。
每组数据第一行一个整数N,表示有N个废弃飞船组成废弃飞船群。
接下来N行每行三个实数,Xi、Yi、Ri表示废弃飞船圆心坐标和半径。
最后一行三个实数表示鸡腿驾驶飞船的圆心位置和半径。
数据保证一开始位置合法。
Output
对于每一组数据,如果可行请输出“YES”,否则输出“NO”,输出后换行。
Sample Input
输入1:
7
2 2 1.1
-2 2 1.1
2 -2 1.0
-2 -2 1.0
2 -5 1.0
0 -8 1.0
-2 -6 1.0
0 0 1
输入2:
5
2 2 1.1
-2 2 1.1
2 -2 1.0
-2 -2 1.0
0 -3 0.01
0 0 1
Sample Output
输出1:
YES
输出2:
NO
Data Constraint
对于30%的数据N ≤ 15;
对于40%的数据 N ≤ 150;
对于 100%的数据 N ≤ 300,数据组数 ≤ 5。
题解
- 乍一看这道题目,一个圆在一对圆中穿梭,只能相离或相切,虽然看懂了题目,但完全无从下手,就这么直接放弃了——
- 若想做出这道题,首先不得不做的是转换题目的条件,很容易(假的)可以想到(我怎么就没想到),把每个废弃的飞船半径加上鸡腿飞船的半径,将鸡腿飞船变成一个点,现在只用看这个点能不能“离开”这片区域。
- 只有一圈的圆把这个点包围起来了,这个点才出不去,否则都是可以的,问题就是怎么找到这“一圈”的圆。
- 先转换坐标,以鸡腿飞船为原点,
- 枚举两个废弃飞船,满足两个条件:在x轴同侧(在x轴上可算作在上方)且两个圆相交,那么就用并查集将这两个圆连边,
- 这样就会出现x轴上方的一些联通块和x轴下方的一些联通块,现在需要看他们中能不能有两块把原点包围。
- 再枚举两个圆,满足两个条件:在x轴异侧且两个圆相交,找到它们连线与x轴的交点,判断是在正半轴还是负半轴,将这两个圆所在的联通块编号之间打上标记,表示在正半轴或负半轴相交了一次,
- 最后枚举两个联通块,如果它们之间能有连线既在正半轴相交且在负半轴相交,那么它们一定能把原点包围,则输出NO,否则输出YES。
代码
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
#define ld long double
#define N 310
struct
{
ld x,y,r;
}a[N];
int f[N],p[N][N][2];
ld sqr(ld x)
{
return x*x;
}
ld dis(int x,int y)
{
return sqrt(sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y));
}
int get(int x)
{
if(f[x]==x) return x;
return f[x]=get(f[x]);
}
int main()
{
int n,i,j;
scanf("%d",&n);
while(n>0)
{
for(i=1;i<=n+1;i++) scanf("%Lf%Lf%Lf",&a[i].x,&a[i].y,&a[i].r);
for(i=1;i<=n;i++) a[i].x-=a[n+1].x,a[i].y-=a[n+1].y,a[i].r+=a[n+1].r;
for(i=1;i<=n;i++) f[i]=i;
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
{
if((a[i].y>=0)!=(a[j].y>=0)) continue;
if(a[i].r+a[j].r>dis(i,j))
{
int x=get(f[i]),y=get(f[j]);
f[x]=y;
}
}
memset(p,0,sizeof(p));
for(i=1;i<n;i++)
for(j=i+1;j<=n;j++)
{
if((a[i].y>=0)==(a[j].y>=0)) continue;
if(a[i].r+a[j].r>dis(i,j))
{
int x=get(f[i]),y=get(f[j]);
ld m=a[i].x;
if(a[i].x!=a[j].x)
{
ld k=(a[i].y-a[j].y)/(a[i].x-a[j].x),b=a[i].y-k*a[i].x;
m=-b/k;
}
if(m<=0) p[x][y][1]=p[y][x][1]=1; else p[x][y][0]=p[y][x][0]=1;
}
}
int ok=1;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++) if(p[i][j][0]&&p[i][j][1]) ok=0;
ok?printf("YES
"):printf("NO
");
n=0;
scanf("%d",&n);
}
return 0;
}