hihoCoder— 穿越禁区(并查集)

题目链接:传送门

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB

描述

作为H国的精英特工,你接到了一项任务,驾驶一辆吉普穿越布满监测雷达的禁区。为了简化题目,我们可以把禁区想象为一个左下角是(0, 0)右上角是( WH )的长方形区域。区域中一共有 N 座雷达,其中第 i 座的坐标是(XiYi ),监测范围是半径为 Ri 的圆形区域。所有在圆内和圆上的运载工具都会被监测到。

你的目标是从左到右穿越禁区。你可以选择线段(0, 0)-(0, H)上任意一点作为起点,线段(W, 0)-(WH)上任意一点作为终点。在禁区内你可以沿任意路线行驶,只要保持始终在禁区内并且没有被雷达监测到。

给出禁区内的雷达部署方案,你需要判断是否存在满足条件的行驶路线。

输入

输入包含多组数据。

第1行是一个整数 T,表示以下有 T 组数据 (1 ≤ T ≤ 10)。

每组数据的第1行:三个整数 WHN (0 ≤ WH ≤ 1000000, 1 ≤ N ≤ 1000)。

每组数据的第2-N+1行:每行三个整数XiYiRi (0 ≤ Xi ≤ W, 0 ≤ Yi ≤ H, 1 ≤ Ri ≤ 1000000)。

输出

对于每组数据输出"YES"或者"NO"表示是否有满足条件的行驶路线。

样例输入
2
10 4 2
5 1 1
5 3 1
10 4 2
5 1 1
6 3 1
样例输出
NO
YES

详细题解:传送门

解题思路:对于多个相交的圆形区域,我们可以将它看成是一个部分,若要使路径不存在,只要任意一个部分的最上端与上边界相交或相切,最下端与下边界相交或相切,即上边界与下边界联通。将圆形区域的联通量化成两个点间的联通,即将所有圆都看成一个点,上下边界各看成是一个点A,B。圆与圆,圆与边界间相交或相切,则两个点间存在一条边使两点联通,于是我们只要使用并查集看A,B是否联通即可。


#include <cstdio>  
#include <cstring>  
#include <cmath>  
#include <iostream>  
#include <queue>
#include <set>
#include <string>
#include <stack>
#include <algorithm>
#include <map>
#include <bitset>
using namespace std;  
typedef long long ll;
const int N = 1008;
const int M = 100000000;
const int INF = 0x3fffffff;
const int mod = 1e9+7;
const double Pi = acos(-1.0);
const double sm = 1e-9;

struct circle{
	int x,y,r;
}cir[N];
int parent[N];

bool cal( circle a , circle b )
{
	ll x = (ll)(a.x-b.x)*(a.x-b.x);
	ll y = (ll)(a.y-b.y)*(a.y-b.y);
	ll r = (ll)(a.r+b.r)*(a.r+b.r);
	return (x+y)<=r;
}

int find( int a )
{
	int b = a;
	while( b != parent[b] ){
		parent[b] = parent[parent[b]];
		b = parent[b];
	}
	return b;
}

void merge( int a , int b )
{
	int i = find(a);
	int j = find(b);
	if( i != j ) parent[i] = j;
}

int main()
{
	int T;
	scanf("%d",&T);
	while( T-- ){
		for( int i = 0 ; i < N ; ++i ) parent[i] = i;
		int w,h,n,s=0,e=1;
		scanf("%d%d%d",&w,&h,&n);
		for( int i = 2 ; i <= n+1 ; ++i ){
			scanf("%d%d%d",&cir[i].x,&cir[i].y,&cir[i].r);
		}
		for( int i = 2 ; i < n+2 ; ++i ){
			if( abs(cir[i].y) <= cir[i].r ) merge(0,i);
			if( abs(h-cir[i].y) <= cir[i].r ) merge(1,i);
		}
		for( int i = 2 ; i < n+1 ; ++i ){
			for( int j = i+1 ; j < n+2 ; ++j ){
				if( cal(cir[i],cir[j] ) ) merge(i,j);
			}
		}
		if( find(0) == find(1) ) printf("NO\n");
		else printf("YES\n");
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值