Problem:
G - 村

問題文

きつねのしえるは，休暇でとある小さな村を訪れている．彼女が驚いたのは，その村の表札がもつ性質である．

村には N 個の家屋がある．ここでは簡単のため，村は 2 次元平面であるとし，家屋はこの平面上の点であると見なす．
それぞれの家屋には表札が 1 個設けられており，その家の苗字を表していた．しえるは村の中を訪問するにつれて，この村の表札が次のような性質を持っていることに気付いた．

ある実数 R が存在する．

もし 2 つの家屋の距離が R 以下であるなら，その 2 つの家屋は同じ表札を持つ．

もし 2 つの家屋の距離が 3R 以上であるなら，その 2 つの家屋は異なる表札を持つ．

任意の 2 つの家屋の距離は R 以下であるか 3R 以上であるかのどちらかである．

ここで，家屋同士の距離は，平面上のユークリッド距離によって計測するものとする．

しえるはこの村に表札が全部で何種類あるのかが気になったが，この村は意外に広いということに気付いたので，計算機を使ってこの答えを模索することにした．

入力形式

入力は以下の形式で与えられる．

N R
x1 y1
x2 y2
...
xN yN

N は家屋の個数である．R は家屋の配置の制約に関する値である．
(xi, yi) は家屋 i の座標を表す．

出力形式

村に存在する表札の種類の数を求めよ．

制約

1 ≤ N ≤ 2 × 105

1 ≤ R ≤ 103

|xi|, |yi| ≤ 106

N は整数である．R, xi, yi は実数で，小数第 3 位まで表される．

任意の 1 ≤ i < j ≤ N に対して dij = √{(xi - xj)2 + (yi - yj)2} とおくとき，dij ≤ R か 3R ≤ dij のどちらかが満たされる．

この問題の判定には，15 点分のテストケースのグループが設定されている． このグループに含まれるテストケースは上記の制約に加えて下記の制約も満たす．

答えは 10 以下である．

入出力例

入力例 1

5 3.000
1.000 0.000
0.000 0.000
-1.000 0.000
10.000 0.000
-10.000 0.000

出力例 1

3

家屋 1,2,3 と家屋 4 と家屋 5 で表札が異なる．全体で 3 つの表札が存在する．

入力例 2

12 1.234
0.500 0.000
-0.500 0.000
0.000 0.000
0.000 -0.500
55.500 55.000
-55.500 55.000
55.000 55.000
55.000 -55.500
99.500 99.000
-99.500 99.000
99.000 99.000
99.000 -99.500

出力例 2

7

入力例 3

5 99.999
0.000 0.000
49.999 0.001
0.000 0.000
-49.999 -0.001
0.000 0.000

出力例 3

1

Writer: 楠本充
Tester: 小浜翔太郎

Solution in JAVA (judge verdict: Time Limit Exceeded)

import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {

    public static void main(String[] args) throws Exception {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        double r = sc.nextDouble();
        Point2D.Double[] ps = new Point2D.Double[n];
        for (int i = 0; i < n; i++) {
            ps[i] = new Point2D.Double(sc.nextDouble(), sc.nextDouble());
        }
        UnionFind uf = new UnionFind(n);
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if ((ps[i].x - ps[j].x) * (ps[i].x - ps[j].x)
                        + (ps[i].y - ps[j].y) * (ps[i].y - ps[j].y) <= r * r)
                    uf.unite(i, j);
            }
        }
        int ans = 0;
        Set<Integer> set = new HashSet<Integer>();
        for (int i = 0; i < n; i++) {
            int m = uf.par[i];
            if (set.contains(m))
                continue;
            set.add(m);
            ans++;
            if (ans == 10)
                break;
        }
        System.out.println(ans);
    }
}

class UnionFind {
    int par[];
    int rank[];

    UnionFind(int n) {
        par = new int[n];
        rank = new int[n];
        for (int i = 0; i < n; i++) {
            par[i] = i;
        }
    }

    public int find(int x) {
        if (x == par[x])
            return x;
        else
            return par[x] = find(par[x]);
    }

    public void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y)
            return;
        if (rank[x] > rank[y])
            par[y] = x;
        else {
            par[x] = y;
            if (rank[x] == rank[y])
                rank[y]++;
        }
    }

    public boolean same(int x, int y) {
        return find(x) == find(y);
    }
}