Luogu3958 [NOIP2017 提高组] 奶酪 题解
传送门:
前置知识
空间内两点 P1(x1,y1,z1)、P2(x2,y2,z2) 的距离公式如下:
dist(P1,P2)=(x1−x2)2+(y1−y2)2+(z1−z2)2
题目大意
地面上有一块无限长宽的奶酪,高度为 h ,其中下表面坐标为 z=0 ,上表面坐标为 z=h 。
奶酪其中有 n 个空洞,半径都为 r,给定每个空洞球心的坐标 (xi,yi,zi) 。如果某两个空洞相交或相切,则这两个空洞为互相连接的。求是否能从奶酪下面走到奶酪上面。(多测)
思路
显然,两个空洞如果互相连接,则它们球心的坐标之差一定 ≤2r 。
根据这个定理,我们求出每两个连接的空洞并记录。
1 2 3 4 5 6 7 8 9 10
| for (int i = 1 ; i <= n ; ++i) { for (int j = 1 ; j <= n ; ++j) { if (sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z)) <= r*2.0) { v[i][j] = 1; } } }
|
并记录空洞是否与奶酪顶或奶酪底相交。如果与奶酪底相交,则添加到搜索起点集合中(这里用的是BFS,将其添加到队列中),如果与奶酪顶相交,则添加到搜索终点集合中。
1 2 3 4 5 6 7 8 9
| if (a[i].z-r <= 0) { q.push(i); } if (a[i].z+r >= h) { v[i][0]=1; v[0][i]=1; }
|
开始 BFS 搜索即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| bool f=0; while (!q.empty()) { int x=q.front(); q.pop(); if (vis[x]) continue; if (v[x][0]) { f=1; break; } vis[x]=1; for (int i = 1 ; i <= n ; ++i) { if (v[x][i]&&!vis[i]) { q.push(i); } } }
if (f) cout << "Yes" << endl; else cout << "No" << endl;
|
复杂度分析
1≤n≤1×103,1≤h,r≤109,T≤20,坐标的绝对值不超过 109。
由于我们有去重操作,所以至多循环 n 次,但我们用了 n2 的复杂度来记录空洞是否连接,所以总复杂度为
O(Tn2)
提示
不开 long long 见祖宗
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| #include <bits/stdc++.h> #define int long long using namespace std; struct node{ int x,y,z; }a[1005]; bool v[1005][1005],vis[1005]; signed main() { freopen("cheese.in","r",stdin); freopen("cheese.out","w",stdout); int T; cin >> T; while (T--) { memset(v,0,sizeof(v)); memset(vis,0,sizeof(vis)); int n,h,r; queue<int> q; cin >> n >> h >> r; for (int i = 1 ; i <= n ; ++i) { cin >> a[i].x >> a[i].y >> a[i].z; if (a[i].z-r <= 0) { q.push(i); } if (a[i].z+r >= h) { v[i][0]=1; v[0][i]=1; } } for (int i = 1 ; i <= n ; ++i) { for (int j = 1 ; j <= n ; ++j) { if (sqrt((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)+(a[i].z-a[j].z)*(a[i].z-a[j].z)) <= r*2.0) { v[i][j] = 1; } } } bool f=0; while (!q.empty()) { int x=q.front(); q.pop(); if (vis[x]) continue; if (v[x][0]) { f=1; break; } vis[x]=1; for (int i = 1 ; i <= n ; ++i) { if (v[x][i]&&!vis[i]) { q.push(i); } } } if (f) cout << "Yes" << endl; else cout << "No" << endl; } return 0; }
|