AtCoder Beginner Contest 259 D题题解

D题链接

题意解释:
有 n 个圈,分别给出坐标(x,y)和半径 r。给两个点的坐标 s(x1,y1)和 t(x2,y2),求是否能至少经过一个圈,让 s 能到 t 点去。

解题思路:
这个可以用并查集来做:
首先,s 可以在有交点的两圆之间移动,所以,有交点的两个圆可以视为一个圆。而 s 和 t 所在的圆可能不止一个,但是由于 n≤3000,所以直接枚举所有 s 和 t 所在的圆就可以。

这样就可以用并查集来做了
用并查集来区分圆,用vector来存 s 和 t 所在的圆的下标。
(一定要用long long ,用double会寄,但是也有玄学用double过的)

代码如下`

#include
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define dec(a,b,c) for(int a=b;a>=c;a--)
#define pb push_back
#define x first
#define y second
#define ios ios::sync_with_stdio(0);cin.tie(0);cout.tie(0) 
#define LL long long
#define PII pair<int,int>
#define INf 0x3f3f3f3f;
#define LNF 0x3f3f3f3f3f3f3f3f;
#define PI acos(-1)
using namespace std;

const int N=3e3+10;
int p[N];
vector<int> v1,v2;

int find(int x)
{
	if(p[x]!=x) p[x]=find(p[x]);
	return p[x];
}

struct Node
{
	LL x,y,r;
}a[N];

LL num(LL a,LL b,LL c,LL d)
{
	return (a-c)*(a-c)+(b-d)*(b-d);
}

LL num(LL a)
{
	return a*a;
}

int main()
{
	ios;
	
	int n;
	cin>>n;
	rep(i,1,n)
		p[i]=i;
		
	int x1,y1,x2,y2;
	cin>>x1>>y1>>x2>>y2;
	
	rep(i,1,n)
	{
		cin>>a[i].x>>a[i].y>>a[i].r;
		rep(j,1,i-1)
		{
			LL k=num(a[i].x,a[i].y,a[j].x,a[j].y);
			if(k<=num(a[i].r+a[j].r)&&k>=num(a[i].r-a[j].r))
			{
				p[find(j)]=find(i);
			}
		}
		
		if(num(a[i].x,a[i].y,x1,y1)==num(a[i].r))
			v1.pb(i);
			
		if(num(a[i].x,a[i].y,x2,y2)==num(a[i].r))
			v2.pb(i);
	}
	for(auto i:v1)
	{
		for(auto j:v2)
			if(find(i)==find(j))
			{
				cout<<"Yes"<<endl;
				return 0;
			}
	}
	
	cout<<"No"<<endl;
	
	return 0;
}

你可能感兴趣的:(算法,c++,数据结构)