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 C++ (judge verdict: Time Limit Exceeded)

#define _USE_MATH_DEFINES
#define INF 0x3f3f3f3f

#include <iostream>
#include <cstdio>
#include <sstream>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <stack>
#include <limits>
#include <map>
#include <string>
#include <cstring>
#include <set>
#include <deque>
#include <bitset>
#include <list>
#include <cctype>
#include <utility>
  
using namespace std;
  
typedef long long ll;
typedef pair <int,int> P;
typedef pair <int,P> PP;
  
static const double EPS = 1e-8;
  
static const int tx[] = {0,1,0,-1};
static const int ty[] = {-1,0,1,0};

class UnionFindTree {
private:
  int* par;
  int* rank;
  int* weight;
public:
  UnionFindTree(int n){
    par = new int[n]();
    rank = new int[n]();
    weight = new int[n]();
    for(int i=0;i<n;i++){
      par[i] = i;
      rank[i] = 0;
      weight[i] = 1;
    }
  }

  ~UnionFindTree(){
    delete[] rank;
    delete[] par;
    delete[] weight;
  }

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

  void unite(int x,int y){
    x = find(x);
    y = find(y);
    if (x == y) return;

    if(rank[x] < rank[y]){
      par[x] = y;
      weight[y] += weight[x];
    } else {
      par[y] = x;
      weight[x] += weight[y];
      if (rank[x] == rank[y]) rank[x]++;
    }
  }

  int count(int x){
    return weight[find(x)];
  }
  bool same(int x,int y){
    return find(x) == find(y);
  }
};

class Home{
public:
  int home_idx;
  double coordinate;
  Home() : home_idx(0),coordinate(0){}
  Home(int _h,double _c) : home_idx(_h),coordinate(_c){}
};

class Point {
public:
  double x;
  double y;
  Point() : x(0), y(0) {}
  Point(double _x,double _y) : x(_x), y(_y) {}
};

int main(){
  int total_homes;
  double R;
  map<double,vector<Home> > x2y;
  map<double,vector<Home> > y2x;
  vector<double> xs;
  vector<double> ys;
  vector<Point> homes;

  while(~scanf("%d %lf",&total_homes,&R)){
    for(int home_idx = 0; home_idx < total_homes; home_idx++){
      double x,y;
      scanf("%lf %lf",&x,&y);
      x2y[x].push_back(Home(home_idx,y));
      y2x[y].push_back(Home(home_idx,x));
      homes.push_back(Point(x,y));
      xs.push_back(x);
      ys.push_back(y);
    }
    
    sort(xs.begin(),xs.end());
    sort(ys.begin(),ys.end());

    UnionFindTree uft(total_homes);
    for(int home_idx = 0; home_idx < total_homes; home_idx++){
      double hx = homes[home_idx].x;
      double hy = homes[home_idx].y;
      int cx_idx = lower_bound(xs.begin(),xs.end(),hx) - xs.begin();
      int cy_idx = lower_bound(ys.begin(),ys.end(),hy) - ys.begin();
      for(int y_idx=cy_idx-2; y_idx <= cy_idx+1; y_idx++){
	if(y_idx < 0 || y_idx >= ys.size()) continue;

	const vector<Home>& h = y2x[ys[y_idx]];
	for(int i=0;i<h.size();i++){
	  int dst_idx = h[i].home_idx;
	  
	  if(dst_idx == home_idx) continue;
	  double dx = h[i].coordinate;
	  double dy = ys[y_idx];
	  if(9 * R * R <= (hx - dx) * (hx - dx) + (hy - dy) * (hy - dy)){
	    continue;
	  }
	  else if(R * R + EPS>= (hx - dx) * (hx - dx) + (hy - dy) * (hy - dy)){
	    // printf("%lf %lf\n",homes[home_idx].x,homes[home_idx].y);
	    // printf("%lf %lf\n\n",homes[dst_idx].x,homes[dst_idx].y);
	    uft.unite(home_idx,dst_idx);
	  }
	}
      }
      for(int x_idx=cx_idx-2; x_idx <= cx_idx+1; x_idx++){
	if(x_idx < 0 || x_idx >= xs.size()) continue;

	const vector<Home>& h = x2y[xs[x_idx]];
	for(int i=0;i<h.size();i++){
	  int dst_idx = h[i].home_idx;
	  if(dst_idx == home_idx) continue;
	  // printf("%lf %lf\n",homes[home_idx].x,homes[home_idx].y);
	  // printf("%lf %lf\n\n",homes[dst_idx].x,homes[dst_idx].y);

	  double dx = xs[x_idx];
	  double dy = x2y[xs[x_idx]][i].coordinate;
	  if(9 * R * R <= (hx - dx) * (hx - dx) + (hy - dy) * (hy - dy)){
	    continue;
	  }
	  else if(R * R + EPS >= (hx - dx) * (hx - dx) + (hy - dy) * (hy - dy)){
	    uft.unite(home_idx,dst_idx);
	  }
	}
      }
    }

    int res = 0;
    bool visited[200001];
    memset(visited,false,sizeof(visited));
    for(int home_idx = 0; home_idx < total_homes; home_idx++){
      if(!visited[uft.find(home_idx)]){
	res++;
	visited[uft.find(home_idx)] = true;
      }
    }
    printf("%d\n",res);
  }
}