NOIp2017Day2T1 乳酪

Dr_Allen發表於2017-12-15

題目描述

現有一塊大乳酪,它的高度為h,它的長度和寬度我們可以認為是無限大的,乳酪中間有許多半徑相同的球形空洞。我們可以在這塊乳酪中建立空間座標系,在座標系中, 乳酪的下表面為z=0,乳酪的上表面為z=h。
現在,乳酪的下表面有一隻小老鼠 Jerry,它知道乳酪中所有空洞的球心所在的坐 標。如果兩個空洞相切或是相交,則 Jerry 可以從其中一個空洞跑到另一個空洞,特別 地,如果一個空洞與下表面相切或是相交,Jerry 則可以從乳酪下表面跑進空洞;如果 一個空洞與上表面相切或是相交,Jerry 則可以從空洞跑到乳酪上表面。
位於乳酪下表面的 Jerry 想知道,在 不破壞乳酪 的情況下,能否利用已有的空洞跑 到乳酪的上表面去?
空間內兩點P1(x1,y1,z1)、P2(x2,y2,z2)的距離公式如下:
dist(P1,P2)=(x1−x2)^2+(y1−y2)^2+(z1−z2)^2

輸入輸出格式

輸入格式:

每個輸入檔案包含多組資料。
輸入檔案的第一行,包含一個正整數T,代表該輸入檔案中所含的資料組數。
接下來是T組資料,每組資料的格式如下: 第一行包含三個正整數n,h和r,兩個數之間以一個空格分開,分別代表乳酪中空洞的數量,乳酪的高度和空洞的半徑。
接下來的n行,每行包含三個整數x,y,z,兩個數之間以一個空格分開,表示空洞球心座標為(x,y,z)。

輸出格式:

輸出檔案包含T行,分別對應T組資料的答案,如果在第i組資料中,Jerry 能從下表面跑到上表面,則輸出Yes,如果不能,則輸出No (均不包含引號)。

輸入輸出樣例

輸入樣例#1:

3
2 4 1
0 0 1
0 0 3
2 5 1
0 0 1
0 0 4
2 5 2
0 0 2
2 0 4

輸出樣例#1:

Yes
No
Yes

說明

【輸入輸出樣例 1 說明】

第一組資料,由乳酪的剖面圖可見,第一個空洞在(0,0,0)與下表面相切,第二個空洞在(0,0,4)與上表面相切 兩個空洞在(0,0,2)相切,輸出 Yes。
第二組資料,由乳酪的剖面圖可見,兩個空洞既不相交也不相切,輸出 No。
第三組資料,由乳酪的剖面圖可見,兩個空洞相交且與上下表面相切或相交,輸出 Yes。
【資料規模與約定】
對於20%的資料,n=1,1≤h,r≤10,000,座標的絕對值不超過 10,000。
對於40%的資料,1≤n≤8,1≤h,r≤10,000,座標的絕對值不超過 10,000。
對於80%的資料,1≤n≤1,000,1≤h,r≤10,000,座標的絕對值不超過10,000。

對於 100%的資料,1≤n≤1,000,1≤h,r≤1,000,000,000,T≤20,座標的絕對值不超過1,000,000,000。

分析

考場上的我。圖論算是一點也不會,所以寫了個20分的暴力,然而莫名其妙多了十分???
簡單解釋解釋。
用並查集,如果兩個洞相交或相切,就把他們的父親連起來。
(當然這之前一定要把他們的父親設定成他們自己)
然後,把n+1作為起點,n+2作為終點,再跑一遍,看看起點終點父親是不是一個。
是一個,很好,老鼠就可以上去。
不是。。老鼠上不去啊
上程式碼吧。
怎麼判斷相交或者相切呢?答案在上面。
不得不說這題比較良心還給了公式。。。
要是學點圖論這題就a了。。。
//蒟蒻習慣寫法,很長
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <iostream>
#include <queue>
#include <vector>
//標頭檔案
using namespace std;
//依然標頭檔案
int fa[1005];
int x[1005];
int y[1005];
int z[1005];
//以上,記錄洞的祖先和圓心座標
int father(int);
bool check(int,int);
//並查集
void start();
void read();
void solve();
void finish();
void write();
//這幾個函式是主體
double pf(double);
//平方函式
int n;
int h;
int r;
//如題
int main()//終於輪到主函式
{

    int tot;//總計

    scanf("%d",&tot);

    while(tot--)//迴圈
    {
        read();//讀入

        start();//初始化父親

        solve();//主體

        finish();//即將結束,遍歷起點終點

        write();//輸出
    }

    return 0;

}

void read()
{

    scanf("%d",&n);
    scanf("%d",&h);
    scanf("%d",&r);

    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
        scanf("%d",&y[i]);
        scanf("%d",&z[i]);
    }
}
//沒啥好說的
void start()
{
    for(int i=1;i<=n+2;i++)
        fa[i]=i;
}
//還是沒啥好說的
void solve()
{
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++)
        {
            if(!check(i,j))
                continue;
            fa[father(i)]=father(j);
        }
}
//也沒啥說的,就是如果他倆相切或相交就連一起
void finish()
{
    for(int i=1; i<=n; i++)
    {
        if(z[i]+r>=h)
            fa[father(n+1)]=father(i);
        if(z[i]-r<=0)
            fa[father(n+2)]=father(i);
    }
}
//起點終點算上一起遍歷
void write()
{
    printf("%s\n",father(n+1)==father(n+2)?"Yes":"No");
}
//輸出
bool check(int a,int b)
{
    if(sqrt(pf(x[a]-x[b])+pf(y[a]-y[b])+pf(z[a]-z[b]))<=2*r)
        return true;
    else
        return false;
}
//看是否相交的
int father(int x)
{
    if(fa[x]==x)
        return x;
    else
        return fa[x]=father(fa[x]);
}
//找爸爸
double pf(double x)
{
    return x*x;
}
//平方