Problem:
最後の扉

秘境の地"ナラマンダ地方"

この奥深い洞窟に眠る黄金の伝説を求め、探検家ボブとマイクは様々な困難に立ち向かいました。ようやくたどり着いた最後の扉。この先には夢に見た黄金が待っています。

その扉には多数の三角形が描かれていました。試しにマイクがその 1 つに触れてみると、その三角形は光を放ちました。光は三角形の底辺から垂直に頂点方向へ長方形を描きながら伸びていき、その先にある別の三角形に触れました。すると、光に触れた三角形も同じように光を放ち、それを繰り返していきました。

扉の三角形は全て二等辺三角形(正三角形は含まない)で、どれも同じ大きさで、どれも重ならず（かつ触れることなく）、またどれも光は一定の距離だけ伸びていました。最後の三角形が光り終えると、そこには何事もなかったかのような静けさが戻ってきました。

ボブは自分の足下に奇妙なメッセージを見つけました。

「むやみに触れるべからず。汝正しくこの扉に触れたとき、この扉は放たれん。 」

ボブとマイクはこのメッセージの意味を考えながら、 何度も三角形に触れてみましたがなかなか扉は開きません。 しかし、それを繰り返していくうちに、二人はメッセージの意味がだんだん分かってきました。

「"むやみに触れるべからず"とは、扉に触れる回数は最小ということ。

できるだけ少ない回数で触れて、全ての三角形を光らせれば、きっと扉は開くはずだ!」

例えば、扉には図 1 のように複数の二等辺三角形が描かれています。

図 1

最初に左下の三角形に触れると、図 2 に示すように、その三角形の底辺から垂直に頂点方向へ光が放たれ、その光が触れる三角形が次々に光ります。

図 2

次に左端の三角形に触れると、図 3 に示すように、全ての三角形が光ります。この手順が全ての三角形を光らせるための最少の手数(2 手)となります。

図 3

さあ、ボブとマイクが最後の扉を開ける手助けをしよう。

扉に描かれている三角形の個数、光の伸びる長さ、それぞれの三角形の頂点の座標を入力とし、全ての三角形を光らせるために扉に触れる最少の回数を出力するプログラムを作成して下さい。光の伸びる長さは、底辺からの長さであるものとします。また、光はそれを放つ三角形の頂点を含むような十分な長さを持ちます。

なお、この問題を解くにあたっては、2 点の距離が 0.01 以下の場合には、同一の点と処理します。また、1 点とひとつの直線の距離が 0.01 以下の場合には、この点はこの直線上にあるとして処理します。

Input

複数のデータセットの並びが入力として与えらる。入力の終わりはゼロふたつの行で示される。各データセットは以下の形式で与えらる。

n d
T1
T2
:
Tn

１行目に扉に描かれている三角形の個数 n (1 ≤ n ≤ 100) と光の伸びる長さを表す整数 d (1 ≤ d ≤ 1000) が与えられる。

続く n 行に各三角形の情報 Ti が以下の形式で与えられる。

x1 y1 x2 y2 x3 y3

三角形の３つの頂点の座標が整数で与えられる(-1000 ≤ x1, y1, x2, y2, x3, y3 ≤ 1000)。

データセットの数は 20 を超えない。

Output

データセットごとに、扉に触れる最少の回数を1行に出力する。

Sample Input

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

Output for the Sample Input

1
3

Solution in JAVA (judge verdict: Accepted)

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

//The Last Door
public class Main{

	int N, D;
	double EPS = 1e-8, T = 1e-2;
	double[][][] p;
	double[][] d;
	int[] top;
	int[][] bottom;
	
	boolean[][] e;
	boolean[][] rev;
	boolean[] visit;
	int ID;
	int[] id;
	int[] scc;
	
	void dfs(int v){
		if(visit[v])return;
		visit[v] = true;
		for(int i=0;i<N;i++)if(e[v][i])dfs(i);
		id[v] = ID++;
	}
	void rdfs(int v){
		if(visit[v])return;
		visit[v] = true;
		scc[v] = ID;
		for(int i=0;i<N;i++)if(rev[v][i])rdfs(i);
	}
	
	boolean hit(int a, int b){
		double[] b1 = p[a][bottom[a][0]], b2 = p[a][bottom[a][1]];
		double[] p1 = new double[]{b1[0]+d[a][0], b1[1]+d[a][1]};
		double[] p2 = new double[]{b2[0]+d[a][0], b2[1]+d[a][1]};
		double area = norm(b1, b2)*D*2;
		for(int j=0;j<3;j++){
			double s = 0;
			s += area(b1, b2, p[b][j]) + area(b2, p2, p[b][j]) + area(p2, p1, p[b][j]) + area(p1, b1, p[b][j]);
			if(Math.abs(s-area)<1e-8)return true;
		}
		for(int j=0;j<3;j++){
			if(dist(b1, b2, p[b][j])<T+EPS)return true;
			if(dist(b2, p2, p[b][j])<T+EPS)return true;
			if(dist(p2, p1, p[b][j])<T+EPS)return true;
			if(dist(p1, b1, p[b][j])<T+EPS)return true;
		}
		for(int j=0;j<3;j++){
			if(dist(p[b][j], p[b][(j+1)%3], b1)<T+EPS)return true;
			if(dist(p[b][j], p[b][(j+1)%3], b2)<T+EPS)return true;
			if(dist(p[b][j], p[b][(j+1)%3], p1)<T+EPS)return true;
			if(dist(p[b][j], p[b][(j+1)%3], p2)<T+EPS)return true;
		}
		for(int j=0;j<3;j++){
			if(crossing(b1, b2, p[b][j], p[b][(j+1)%3]))return true;
			if(crossing(b2, p2, p[b][j], p[b][(j+1)%3]))return true;
			if(crossing(p2, p1, p[b][j], p[b][(j+1)%3]))return true;
			if(crossing(p1, b1, p[b][j], p[b][(j+1)%3]))return true;
		}
		return false;
	}
	double ex(double[] a, double[] b, double[] c){
		double[] s1 = sub(b, a), s2 = sub(c, a);
		return cross(s1, s2);
	}
	boolean crossing(double[] a, double[] b, double[] s, double[] t){
		if(ex(a, b, s)*ex(a, b, t)>0)return false;
		if(ex(b, a, s)*ex(b, a, t)>0)return false;
		if(ex(s, t, a)*ex(s, t, b)>0)return false;
		return ex(t, s, a)*ex(t, s, b)<0;
	}
	double dist(double[] a, double[] b, double[] p){
		if(dot(sub(b, a), sub(p, a))<EPS)return norm(a, p);
		if(dot(sub(a, b), sub(p, b))<EPS)return norm(b, p);
		return Math.abs(cross(sub(b, a), sub(p, a)))/norm(a, b);
	}
	double dot(double[] a, double[] b){
		return a[0]*b[0]+a[1]*b[1];
	}
	double cross(double[] a, double[] b){
		return a[0]*b[1]-a[1]*b[0];
	}
	double norm(double[] a, double[] b){
		return Math.hypot(a[0]-b[0], a[1]-b[1]);
	}
	double[] sub(double[] a, double[] b){
		return new double[]{a[0]-b[0], a[1]-b[1]};
	}
	double[] mid(double[] a, double[] b){
		return new double[]{(a[0]+b[0])/2, (a[1]+b[1])/2};
	}
	double area(double[] a, double[] b, double[] c){
		double res = cross(a, b)+cross(b, c)+cross(c, a);
		return Math.abs(res);
	}
	
	void run(){
		Scanner sc = new Scanner(System.in);
		for(;;){
			N = sc.nextInt(); D = sc.nextInt();
			if((N|D)==0)break;
			p = new double[N][3][2];
			d = new double[N][2];
			top = new int[N];
			bottom = new int[N][2];
			for(int i=0;i<N;i++){
				for(int j=0;j<3;j++)for(int k=0;k<2;k++)p[i][j][k] = sc.nextDouble();
				for(int j=0;j<3;j++){
					if(Math.abs(norm(p[i][j], p[i][(j+1)%3])-norm(p[i][j], p[i][(j+2)%3]))<EPS){
						top[i] = j;
						bottom[i][0] = (j+1)%3; bottom[i][1] = (j+2)%3;
						double[] m = mid(p[i][(j+1)%3], p[i][(j+2)%3]);
						d[i] = sub(p[i][j], m);
						double nor = Math.hypot(d[i][0], d[i][1]);
						d[i][0]/=nor; d[i][1]/=nor;
						d[i][0]*=D; d[i][1]*=D;
						break;
					}
				}
			}
			e = new boolean[N][N];
			rev = new boolean[N][N];
			for(int i=0;i<N;i++)for(int j=0;j<N;j++){
				if(i==j)continue;
				e[i][j] = hit(i, j);
				rev[j][i] = e[i][j];
			}
			visit = new boolean[N];
			ID = 0;
			id = new int[N];
			for(int i=0;i<N;i++){
				if(visit[i])continue;
				dfs(i);
			}
			PriorityQueue<Integer> q = new PriorityQueue<Integer>(N, new Comparator<Integer>() {
				public int compare(Integer o1, Integer o2) {
					return id[o2]-id[o1];
				}
			});
			for(int i=0;i<N;i++)q.add(i);
			scc = new int[N];
			ID = 0;
			Arrays.fill(visit, false);
			while(!q.isEmpty()){
				int v = q.poll();
				if(visit[v])continue;
				rdfs(v);
				ID++;
			}
			int[] deg = new int[ID];
			for(int i=0;i<N;i++)for(int j=0;j<N;j++){
				if(i==j||scc[i]==scc[j]||!e[i][j])continue;
				deg[scc[j]]++;
			}
			int res = 0;
			for(int i=0;i<ID;i++)if(deg[i]==0)res++;
			System.out.println(res);
		}
	}
	
	public static void main(String[] args) {
		new Main().run();
	}
}