【题解】P3958 [NOIP2017 提高组] 奶酪
本题解仿照联合国大会决议写成。
联合国大会第一百一十四届会议
议程项目一:一系列信息题目的解法
2025 年 1 月 4 日大会决议
[未经发交主要委员会而通过]
114/514. 题目 P3958 的解法
大会,
注意到题目给出了两个奶酪相连通的条件,即两球相切或相交。
又注意到题目询问是否能从下表面洞走到上表面洞,即询问某两个洞是否连通。
回顾并查集的作用,即连接一系列点,然后判断某两个点是否连通。恰好是本题所要解决的问题。
考虑连通即两球相切或相交的条件,显然当两球的球心距离小于两球半径之和时,两圆相切或相交。
又考虑一个洞位于上表面的条件,是最上端顶点 坐标即 大于等于 。
还考虑一个洞位于下表面的条件,是最下端顶点 坐标即 小于等于 。
建议两两枚举所有的洞,将所有洞在并查集中连通。然后遍历所有洞,筛选出位于上表面和下表面的点。接着遍历每个上表面的点和下表面的点并判断是否连通。最后输出即可。
还建议使用 long long 类型,因为在计算距离的时候两个 相乘会超过 int 类型的范围。
观察到这样的做法复杂度是 ,可以通过本题。
强调“多测不清空,爆零两行泪”这一事实。
给出下列可以通过的代码:
#include <iostream>
#include <cmath>
#define int long long
using namespace std;
const int N = 1e5 + 10;
int T;
int x[N], y[N], z[N], xbm[N], sbm[N], p, q;
int fa[N];
int find(int x)
{
return (fa[x] == x ? x : fa[x] = find(fa[x]));
}
signed main()
{
cin >> T;
while(T--)
{
int n, h, r;
cin >> n >> h >> r;
for(int i = 1;i <= n;i++)
{
cin >> x[i] >> y[i] >> z[i];
fa[i] = i;
}
p = 0;
q = 0;
for(int i = 1;i <= n;i++)
{
for(int j = 1;j < i;j++)
{
if(sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]) + (z[i] - z[j]) * (z[i] - z[j])) <= r + r)
{
int fi = find(i), fj = find(j);
if(fi != fj) fa[fi] = fj;
}
}
if(z[i] - r <= 0) xbm[++p] = i;
if(z[i] + r >= h) sbm[++q] = i;
}
bool ok = 0;
for(int i = 1;i <= p;i++)
{
for(int j = 1;j <= q;j++)
{
if(find(xbm[i]) == find(sbm[j]))
{
ok = 1;
break;
}
}
if(ok) break;
}
cout << (ok ? "Yes" : "No") << endl;
}
return 0;
}
2025 年 1 月 4 日
第七十次全体会议
d8748c4a-f8c7-49bb-b8ec-218bb66fd4d0

浙公网安备 33010602011771号