Problem:
秋のイルミネーション

四角形が大好きな人が多く住む四角町では、四角形の電光板を組み合わせたイルミネーションで町中を彩るお祭りが開催されます。この電光板は電気を流すと発光し、発光している板に接している板も発光します。したがって、電光板を何枚使っても、すべてがひとかたまりに接していれば電源がひとつで済むという優れものです。

お祭り実行委員会では、四角形を組み合わせたデザインを町民から募集し多数のイルミネーションを作成します。各イルミネーションは、デザインを構成する四角形の配置によって必要となる電源の個数が変わるため、必要な電源の個数を把握する作業に大きな手間がかかります。そこで、あなたは四角形の配置を調べ、必要な電源の個数を計算するプログラムを作成して実行委員会を手助けすることにしました。

イルミネーションの個数、各イルミネーションを構成する四角形の情報を入力とし、イルミネーションごとに必要な電源の個数を出力するプログラムを作成してください。重なっていたり接していたりする四角形を 1 つのまとまりと考え、1 つのまとまりに対して電源の個数は 1 つと数えてください。

Input

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

N
M1
xa1 ya1 xb1 yb1 xc1 yc1 xd1 yd1
xa2 ya2 xb2 yb2 xc2 yc2 xd2 yd2
:
xaM1 yaM1 xbM1 ybM1 xcM1 ycM1 xdM1 ydM1
M2
:
:
MN
:
:

1 行目にイルミネーションの個数 N (1 ≤ N ≤ 100) が与えられます。続いて、N 個のイルミネーションの情報が与えられます。第 i のイルミネーションの情報として、イルミネーションを構成する四角形の個数 Mi (1 ≤ Mi ≤ 100) が１行に与えられます。続く Mi 行に j 個目の四角形の頂点 xaj, yaj, xbj, ybj, xcj, ycj, xdj, ydj (-1000 以上 1000 以下の整数) が与えられます。

入力される四角形の頂点座標は、時計回りの順に従って入力されます。ただし、入力データの四角形はすべて凸な四角形とします。

データセットの数は 10 を超えません。

Output

入力データセットごとに、イルミネーションごとに必要な電源の個数を出力します。出力する電源の個数は各イルミネーションが入力された順番に従ってください。

Sample Input

3
1
0 0 0 1 1 1 1 0
2
0 0 0 1 1 1 1 0
100 100 100 200 200 200 200 100
2
0 0 0 100 100 100 100 0
10 10 10 20 20 20 20 10
2
1
30 40 40 50 40 20 20 10
1
30 40 40 50 40 20 20 10
0

Output for the Sample Input

1
2
1
1
1

Solution in JAVA (judge verdict: Accepted)

import java.awt.geom.Line2D;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {
    final double pi = (1 << 2)
            * (Math.cbrt(1. / 0.015625) * Math.atan(0.2) - Math.atan(1. / 239));
    final double EPS = 1.0e-10;

    void run() {
        Scanner sc = new Scanner(System.in);
        while (true) {
            int n = sc.nextInt();
            if (n == 0)
                break;
            while (n != 0) {
                int m = sc.nextInt();
                Tetragon[] rs = new Tetragon[m];
                for (int i = 0; i < m; i++) {
                    P[] ps = new P[4];
                    for (int j = 0; j < 4; j++)
                        ps[j] = new P(sc.nextInt(), sc.nextInt());
                    rs[i] = new Tetragon(ps);
                }
                UnionFind uf = new UnionFind(m);
                for (int i = 0; i < m; i++) {
                    for (int j = 0; j < m; j++) {
                        if (i == j)
                            continue;
                        if (rs[i].isTouch(rs[j]))
                            uf.unite(i, j);
                    }
                }
                Set<Integer> set = new HashSet<Integer>();
                for (int i = 0; i < m; i++) {
                    set.add(uf.find(i));
                }
                System.out.println(set.size());
                n--;
            }
        }
    }

    public static void main(String[] args) {
        new Main().run();
    }

}

class P {
    int x;
    int y;

    P(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String toString() {
        return "(" + x + " , " + y + ")";
    }

    public P add(P p) {
        return new P(p.x + x, p.y + y);
    }

    public P sub(P p) {
        return new P(x - p.x, y - p.y);
    }

    public P mul(int k) {
        return new P(k * x, k * y);
    }

    public double abs() {
        return Math.sqrt(x * x + y * y);
    }

    public int abs2() {
        return x * x + y * y;
    }

    public int det(P p) {
        return x * p.y - y * p.x;
    }

    public P rot90() {
        return new P(-y, x);
    }

    boolean inTetragon(Tetragon t) {
        boolean right = true;
        boolean left = true;
        for (int i = 0; i < 4; i++) {
            int ep = this.sub(t.ps[i]).det(t.ps[(i + 1) % 4].sub(t.ps[i]));
            if (ep < 0)
                left = false;
            else if (ep > 0)
                right = false;
        }
        return right || left;
    }
}

class Tetragon {
    P ps[] = new P[4];

    Tetragon(P qs[]) {
        for (int i = 0; i < 4; i++) {
            ps[i] = qs[i];
        }
    }

    boolean isTouch(Tetragon t) {
        for (int i = 0; i < 4; i++) {
            if (ps[i].inTetragon(t))
                return true;
        }
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                int x1 = ps[i].x;
                int y1 = ps[i].y;
                int x2 = ps[(i + 1) % 4].x;
                int y2 = ps[(i + 1) % 4].y;
                int x3 = t.ps[j].x;
                int y3 = t.ps[j].y;
                int x4 = t.ps[(j + 1) % 4].x;
                int y4 = t.ps[(j + 1) % 4].y;
                int tc = (x1 - x2) * (y3 - y1) + (y1 - y2) * (x1 - x3);
                int td = (x1 - x2) * (y4 - y1) + (y1 - y2) * (x1 - x4);
                int ta = (x3 - x4) * (y1 - y3) + (y3 - y4) * (x3 - x1);
                int tb = (x3 - x4) * (y2 - y3) + (y3 - y4) * (x3 - x2);

                if (tc * td < 0 && ta * tb < 0)
                    return true;
            }
        }
        return false;
    }
}

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;
        }
    }

    int find(int x) {
        if (x == par[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[y] > rank[x])
            par[x] = y;
        else {
            par[y] = x;
            if (rank[x] == rank[y])
                rank[x]++;
        }
    }

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