l i n k link link
给你一些圆,再给出两个点,两个点均为圆上,你可以走在圆上的任意位置,问这两个点是否联通?
这个题和今年蓝桥杯省赛的一道题扫雷背景很像,但是做法不同。
根据题目,我们其实能理解一个事情:只要两个圆相交或相切,代表着两个圆互通。
如果另外有一个圆和这两个圆的任意一个圆有交集,那么这三个圆互通。
那么我们就可以想到使用并查集维护这一个性质,如果两个圆相交就使用并查集合并。
现在问题转化成了:如何判断两个圆相交?
设两个圆的圆心距为 p p p,半径分别是 R , r R,r R,r
相交: R − r < p < P + r R-r R−r<p<P+r
外切: p = R + r p=R+r p=R+r
内切: p = R − r p=R-r p=R−r
也就是只要满足 R − r ≤ p ≤ R + r R-r\le p\le R+r R−r≤p≤R+r即可合并。
#include
using namespace std;
#define int long long
struct node
{
int x, y, r;
} s[210210];
bool ok(int x, int y, int xx, int yy, int r)
{
if ((x - xx) * (x - xx) + (y - yy) * (y - yy) == r * r)
return 1;
return 0;
}
bool ok2(int x, int y, int r2, int xx, int yy, int r)
{
if ((x - xx) * (x - xx) + (y - yy) * (y - yy) <= (r + r2) * (r + r2) && (r - r2) * (r - r2) <= (x - xx) * (x - xx) + (y - yy) * (y - yy))
return 1;
return 0;
}
vector<int> ans1;
vector<int> ans2;
int a[2102100];
int find(int x)
{
if (a[x] != x)
a[x] = find(a[x]);
return a[x];
}
signed main()
{
int n;
cin >> n;
int sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
for (int i = 1; i <= n; i++)
{
a[i] = i;
cin >> s[i].x >> s[i].y >> s[i].r;
if (ok(sx, sy, s[i].x, s[i].y, s[i].r))
ans1.push_back(i);
if (ok(tx, ty, s[i].x, s[i].y, s[i].r))
ans2.push_back(i);
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (ok2(s[i].x, s[i].y, s[i].r, s[j].x, s[j].y, s[j].r))
a[find(i)] = find(j);
}
}
for (int i = 0; i < ans1.size(); i++)
{
for (int j = 0; j < ans2.size(); j++)
{
if (find(ans1[i]) == find(ans2[j]))
{
cout << "Yes" << endl;
return 0;
}
}
}
cout << "No" << endl;
}