Problem:
秋のイルミネーション
四角形が大好きな人が多く住む四角町では、四角形の電光板を組み合わせたイルミネーションで町中を彩るお祭りが開催されます。
この電光板は電気を流すと発光し、発光している板に接している板も発光します。したがって、電光板を何枚使っても、すべてがひとかたまりに接していれば電源がひとつで済むという優れものです。
お祭り実行委員会では、四角形を組み合わせたデザインを町民から募集し多数のイルミネーションを作成します。各イルミネーションは、デザインを構成する四角形の配置によって必要となる電源の個数が変わるため、必要な電源の個数を把握する作業に
大きな手間がかかります。
そこで、
あなたは四角形の配置を調べ、必要な電源の個数を計算するプログラムを作成して実行委員会を手助けすることにしました。
イルミネーションの個数、各イルミネーションを構成する四角形の情報を入力とし、イルミネーションごとに必要な電源の個数を出力するプログラムを作成してください。重なっていたり接していたりする四角形を 1 つのまとまりと考え、1 つのまとまりに対して電源の個数は 1 つと数えてください。
Input
複数のデータセットの並びが入力として与えられます。入力の終わりはゼロひとつの行で示されます。各データセットは以下の形式で与えられます。
N
M
1
xa
1
ya
1
xb
1
yb
1
xc
1
yc
1
xd
1
yd
1
xa
2
ya
2
xb
2
yb
2
xc
2
yc
2
xd
2
yd
2
:
xa
M
1
ya
M
1
xb
M
1
yb
M
1
xc
M
1
yc
M
1
xd
M
1
yd
M
1
M
2
:
:
M
N
:
:
1 行目にイルミネーションの個数
N
(1 ≤
N
≤ 100) が与えられます。続いて、
N
個のイルミネーションの情報が与えられます。第
i
のイルミネーションの情報として、イルミネーションを構成する四角形の個数
M
i
(1 ≤
M
i
≤ 100) が１行に与えられます。続く
M
i
行に
j
個目の四角形の頂点
xa
j
,
ya
j
,
xb
j
,
yb
j
,
xc
j
,
yc
j
,
xd
j
,
yd
j
(-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

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

#include <algorithm>
#include <cmath>
#include <iostream>
#include <iterator>
#include <utility>
#include <vector>

using Real = long double;
constexpr Real EPS = 1e-10;
const Real PI = std::acos(-1);

int sign(Real x) {
    return x < -EPS ? -1 : x > EPS ? 1 : 0;
}

bool eq(Real lhs, Real rhs) {
    return sign(rhs - lhs) == 0;
}

bool lte(Real lhs, Real rhs) {
    return sign(rhs - lhs) >= 0;
}

bool lt(Real lhs, Real rhs) {
    return sign(rhs - lhs) > 0;
}

bool gte(Real lhs, Real rhs) {
    return lte(rhs, lhs);
}

bool gt(Real lhs, Real rhs) {
    return lt(rhs, lhs);
}

struct Point {
    Real x;
    Real y;
    Point(Real x = 0, Real y = 0): x(x), y(y) {}
    Point& operator+=(Point rhs) {
        this->x += rhs.x;
        this->y += rhs.y;
        return *this;
    }
    Point& operator-=(Point rhs) {
        this->x -= rhs.x;
        this->y -= rhs.y;
        return *this;
    }
    Point& operator*=(Real k) {
        this->x *= k;
        this->y *= k;
        return *this;
    }
    Point& operator/=(Real k) {
        this->x /= k;
        this->y /= k;
        return *this;
    }
    friend Point operator+(Point lhs, Point rhs) {
        return lhs += rhs;
    }
    friend Point operator-(Point lhs, Point rhs) {
        return lhs -= rhs;
    }
    friend Point operator*(Point p, Real k) {
        return p *= k;
    }
    friend Point operator/(Point p, Real k) {
        return p /= k;
    }
    friend Point operator*(Real k, Point p) {
        return p * k;
    }
    friend Point operator/(Real k, Point p) {
        return p / k;
    }
    friend bool operator==(Point lhs, Point rhs) {
        return eq(lhs.x, rhs.x) && eq(lhs.y, rhs.y);
    }
    friend bool operator<(Point lhs, Point rhs) {
        if (eq(lhs.x, rhs.x)) return lt(lhs.y, rhs.y);
        return lt(lhs.x, rhs.x);
    }
    friend bool operator>(Point lhs, Point rhs) {
        return rhs < lhs;
    }
    friend std::istream& operator>>(std::istream& is, Point& p) {
        is >> p.x >> p.y;
        return is;
    }
    friend std::ostream& operator<<(std::ostream& os, Point& p) {
        os << p.x << ' ' << p.y;
        return os;
    }
};

using Points = std::vector<Point>;
using Polygon = Points;
using Vector = Point;

Real norm(Vector p) {
    return p.x * p.x + p.y * p.y;
}

Real abs(Vector p) {
    return std::sqrt(norm(p));
}

Real dot(Vector lhs, Vector rhs) {
    return lhs.x * rhs.x + lhs.y * rhs.y;
}

Real cross(Vector lhs, Vector rhs) {
    return lhs.x * rhs.y - lhs.y * rhs.x;
}

int ccw(Point p0, Point p1, Point p2) {
    Vector a = p1 - p0;
    Vector b = p2 - p0;

    if (cross(a, b) > EPS) return 1;

    if (cross(a, b) < -EPS) return -1;

    if (dot(a, b) < 0) return 2;

    if (norm(a) < norm(b)) return -2;

    return 0;
}

struct Circle {
    Real r;
    Point c;
    Circle() {}
    Circle(Real r, Point c): r(r), c(c) {}
};

struct Segment {
    Segment() {}
    Segment(Point p0, Point p1): p0(p0), p1(p1) {}
    Point p0, p1;
};

struct Line {
    Line() {}
    Line(Point p0, Point p1): p0(p0), p1(p1) {}
    explicit Line(Segment s): p0(s.p0), p1(s.p1) {}
    Point p0, p1;
};

Real distance(Point lhs, Point rhs) {
    return abs(rhs - lhs);
}

Real distance(Line l, Point p) {
    return std::abs(cross(l.p1 - l.p0, p - l.p0)) / abs(l.p1 - l.p0);
}

Real distance(Segment s, Point p) {
    if (dot(s.p1 - s.p0, p - s.p0) < 0) {
        return distance(p, s.p0);
    }
    if (dot(s.p0 - s.p1, p - s.p1) < 0) {
        return distance(p, s.p1);
    }
    return distance(Line(s), p);
}

bool intersect(Segment lhs, Segment rhs) {
    return ccw(lhs.p0, lhs.p1, rhs.p0) * ccw(lhs.p0, lhs.p1, rhs.p1) <= 0
           && ccw(rhs.p0, rhs.p1, lhs.p0) * ccw(rhs.p0, rhs.p1, lhs.p1) <= 0;
}

bool intersect(Segment s, Point p) {
    return ccw(s.p0, s.p1, p) == 0;
}

Real distance(Segment lhs, Segment rhs) {
    if (intersect(lhs, rhs)) {
        return Real(0);
    }
    return std::min({distance(lhs, rhs.p0), distance(lhs, rhs.p1), distance(rhs, lhs.p0), distance(rhs, lhs.p1)});
}

bool parallel(Vector lhs, Vector rhs) {
    return eq(cross(lhs, rhs), 0);
}

bool parallel(Segment lhs, Segment rhs) {
    return parallel(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

bool parallel(Line lhs, Line rhs) {
    return parallel(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

Point crosspoint(Line lhs, Line rhs) {
    Real a = cross(lhs.p1 - lhs.p0, rhs.p1 - rhs.p0);
    Real b = cross(lhs.p1 - lhs.p0, lhs.p1 - rhs.p0);
    return rhs.p0 + (rhs.p1 - rhs.p0) * b / a;
}

bool intersect(Line l, Segment s) {
    return ccw(l.p0, l.p1, s.p0) * ccw(l.p0, l.p1, s.p1) <= 0;
}

bool intersect(Circle c, Line l) {
    return lte(distance(l, c.c), c.r);
}

bool intersect(Circle c, Point p) {
    return eq(distance(c.c, p), c.r);
}

bool internal(Circle c, Point p) {
    return lt(distance(c.c, p), c.r);
}

bool intersect(Circle c, Segment s) {
    return lte(distance(s, c.c), c.r);
}

int intersect(Circle lhs, Circle rhs) {
    if (gt(lhs.r, rhs.r)) std::swap(lhs, rhs);
    Real d = distance(lhs.c, rhs.c);
    if (lt(lhs.r + rhs.r, d)) return 4;
    if (eq(lhs.r + rhs.r, d)) return 3;
    if (gt(lhs.r + d, rhs.r)) return 2;
    if (eq(lhs.r + d, rhs.r)) return 1;
    return 0;
}

bool orthogonal(Vector lhs, Vector rhs) {
    return eq(dot(lhs, rhs), 0);
}

bool orthogonal(Segment lhs, Segment rhs) {
    return orthogonal(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

bool orthogonal(Line lhs, Line rhs) {
    return orthogonal(lhs.p0 - lhs.p1, rhs.p0 - rhs.p1);
}

Point crosspoint(Segment lhs, Segment rhs) {
    Real d0 = distance(Line(lhs.p0, lhs.p1), rhs.p0);
    Real d1 = distance(Line(lhs.p0, lhs.p1), rhs.p1);
    return rhs.p0 + (rhs.p1 - rhs.p0) * (d0 / (d0 + d1));
}

Real arg(Vector p) {
    return std::atan2(p.y, p.x);
}

Vector polar(Real a, Real r) {
    return Point(std::cos(r) * a, std::sin(r) * a);
}

Point rotate(Point p, Real t) {
    Point v = polar(1, t);
    return Point(p.x * v.x - p.y * v.y, p.x * v.y + p.y * v.x);
}

Real angle(Point p0, Point p1, Point p2) {
    Real a = arg(p0 - p1);
    Real b = arg(p2 - p1);
    if (gt(a, b)) std::swap(a, b);
    return std::min(b - a, 2 * PI - (b - a));
}

Points crosspoint(Circle lhs, Circle rhs) {
    Real d = abs(lhs.c - rhs.c);
    if (eq(d, lhs.r + rhs.r)) return {lhs.c + lhs.r * (rhs.c - lhs.c) / d};
    Real a = std::acos((lhs.r * lhs.r + d * d - rhs.r * rhs.r) / (2 * lhs.r * d));
    Real t = arg(rhs.c - lhs.c);
    return {lhs.c + polar(lhs.r, t + a), lhs.c + polar(lhs.r, t - a)};
}

Point projection(Segment s, Point p) {
    Vector a = p - s.p0;
    Vector b = s.p1 - s.p0;
    Real t = dot(a, b) / norm(b);
    return s.p0 + t * b;
}

Point projection(Line l, Point p) {
    Vector a = p - l.p0;
    Vector b = l.p1 - l.p0;
    Real t = dot(a, b) / norm(b);
    return l.p0 + t * b;
}

Point reflection(Segment s, Point p) {
    return p + (projection(s, p) - p) * Real(2);
}

Point reflection(Line l, Point p) {
    return p + (projection(l, p) - p) * Real(2);
}

Points crosspoint(Circle c, Line l) {
    Vector p = projection(l, c.c);
    Real t = std::sqrt(c.r * c.r - norm(c.c - p));
    if (eq(t, 0)) return {p};
    Vector e = (l.p1 - l.p0) / abs(l.p1 - l.p0);
    return {p + t * e, p + -t * e};
}

Real area(Polygon poly) {
    int n = poly.size();
    Real result = 0;
    for (int i = 0; i < n; i++) {
        result += cross(poly[i], poly[(i + 1) % n]);
    }
    return result / 2;
}

Real perimeter(Polygon poly) {
    int n = poly.size();
    Real result = 0;
    for (int i = 0; i < n; i++) {
        result += abs(poly[i] - poly[(i + 1) % n]);
    }
    return result;
}

bool convex(Polygon poly) {
    int n = poly.size();
    for (int i = 0; i < n; i++) {
        if (ccw(poly[i], poly[(i + 1) % n], poly[(i + 2) % n]) == -1) return false;
    }
    return true;
}

int contain(Polygon poly, Point p) {
    int n = poly.size();
    bool parity = false;
    for (int i = 0; i < n; i++) {
        Point a = poly[i] - p;
        Point b = poly[(i + 1) % n] - p;
        if (gt(a.y, b.y)) std::swap(a, b);
        if (lte(a.y, 0) && lt(0, b.y) && lt(cross(a, b), 0)) parity ^= true;
        if (eq(cross(a, b), 0) && lte(dot(a, b), 0)) return 1;
    }
    return (parity ? 2 : 0);
}

Polygon convex_hull(Points points) {
    std::sort(points.begin(), points.end(), [](Point lhs, Point rhs) {
        if (eq(lhs.x, rhs.x)) return lt(lhs.y, rhs.y);
        return lt(lhs.x, rhs.x);
    });
    int n = points.size();
    Points lower, upper;
    lower.push_back(points[n - 1]);
    lower.push_back(points[n - 2]);
    upper.push_back(points[0]);
    upper.push_back(points[1]);
    for (int i = n - 3; i >= 0; i--) {
        for (int m = lower.size(); m >= 2 && ccw(lower[m - 2], lower[m - 1], points[i]) >= 0; m--) {
            lower.pop_back();
        }
        lower.push_back(points[i]);
    }
    for (int i = 2; i < n; i++) {
        for (int m = upper.size(); m >= 2 && ccw(upper[m - 2], upper[m - 1], points[i]) >= 0; m--) {
            upper.pop_back();
        }
        upper.push_back(points[i]);
    }
    std::reverse(lower.begin(), lower.end());
    std::copy(std::next(upper.rbegin()), std::prev(upper.rend()), std::back_inserter(lower));
    return lower;
}

Polygon convex_cut(Polygon poly, Line l) {
    int n = poly.size();
    Polygon res;
    for (int i = 0; i < n; i++) {
        Point a = poly[i];
        Point b = poly[(i + 1) % n];
        if (ccw(l.p0, l.p1, a) != -1) res.push_back(a);
        if (ccw(l.p0, l.p1, a) * ccw(l.p0, l.p1, b) == -1) {
            res.push_back(crosspoint(l, Line(a, b)));
        }
    }
    return res;
}

Line bisector(Point p0, Point p1, Point p2) {
    return Line(p1, p1 + polar(1, angle(p0, p1, p2) / 2));
}

Circle incircle(Point p0, Point p1, Point p2) {
    Point c = crosspoint(bisector(p0, p1, p2), bisector(p1, p2, p0));
    Real r = std::abs(2 * area({p0, p1, p2}) / perimeter({p0, p1, p2}));
    return Circle(r, c);
}

Line perpendicular(Line l, Point p) {
    Vector v = l.p1 - l.p0;
    return Line(p, p + Vector(-v.y, v.x));
}

Line perpendicular_bisector(Segment s) {
    return perpendicular(Line(s), (s.p0 + s.p1) / 2);
}

Circle circumscribed_circle(Point p0, Point p1, Point p2) {
    Point c = crosspoint(perpendicular_bisector(Segment(p0, p1)), perpendicular_bisector(Segment(p0, p2)));
    return Circle(distance(p0, c), c);
}

Real area(Circle c) {
    return c.r * c.r * PI;
}

Points tangent(Circle c, Point p) {
    return crosspoint(c, Circle(std::sqrt(norm(c.c - p) - c.r * c.r), p));
}

struct UnionFind {
    int n;
    int g;
    std::vector<int> data;
    UnionFind(int n): n(n), g(n), data(n, -1) {}
    int leader(int x) {
        if (data[x] < 0) return x;
        return data[x] = leader(data[x]);
    }
    void merge(int lhs, int rhs) {
        lhs = leader(lhs);
        rhs = leader(rhs);
        if (lhs == rhs) return;
        if (data[lhs] < data[rhs]) std::swap(lhs, rhs);
        data[rhs] += data[lhs];
        data[lhs] = rhs;
        g--;
    }
    int group_num() {
        return g;
    }
};

int solve() {
    int n;
    std::cin >> n;
    if (n == 0) return 1;
    while (n--) {
        int m;
        std::cin >> m;
        std::vector<Polygon> ps;
        for (int i = 0; i < m; i++) {
            Polygon p;
            for (int j = 0; j < 4; j++) {
                Point q;
                std::cin >> q;
                p.push_back(q);
            }
            std::reverse(p.begin(), p.end());
            ps.push_back(p);
        }
        UnionFind uf(m);
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < m; j++) {
                if (i == j) continue;

                {
                    bool ok = true;
                    for (int k = 0; k < 4; k++) {
                        ok &= contain(ps[i], ps[j][k]) > 0;
                    }
                    if (ok) uf.merge(i, j);
                }

                {
                    bool ok = false;
                    for (int k = 0; k < 4; k++) {
                        Segment s(ps[i][k], ps[i][(k + 1) % 4]);
                        for (int l = 0; l < 4; l++) {
                            Segment t(ps[j][l], ps[j][(l + 1) % 4]);
                            ok |= intersect(s, t);
                        }
                    }
                    if (ok) uf.merge(i, j);
                }
            }
        }
        std::cout << uf.group_num() << '\n';
    }
    return 0;
}

int main() {
    while (!solve())
        ;
}
accuracy: 1
time_ms: 20
memory_kb: 3312
final_rank: 15

code_snippet: #line 1 "a.cpp"
using namespace std;
#line 1 "/home/kotatsugame/library/datastructure/UF.cpp"
#include<vector>
struct UF{
	int n;
	vector<int>parent,rank;
	UF(int n_=0):n(n_),parent(n_),rank(n_,1)
	{
		for(int i=0;i<n_;i++)parent[i]=i;
	}
	int find(int a){return parent[a]!=a?parent[a]=find(parent[a]):a;}
	bool same(int a,int b){return find(a)==find(b);}
	bool unite(int a,int b)
	{
		a=find(a),b=find(b);
		if(a==b)return false;
		if(rank[a]<rank[b])
		{
			parent[a]=b;
			rank[b]+=rank[a];
		}
		else
		{
			parent[b]=a;
			rank[a]+=rank[b];
		}
		return true;
	}
	int size(int a){return rank[find(a)];}
};
#line 1 "/home/kotatsugame/library/math/point.cpp"
#define _USE_MATH_DEFINES
#include<iostream>
#include<algorithm>
#line 5 "/home/kotatsugame/library/math/point.cpp"
#include<cmath>
#include<iomanip>
const double EPS=1e-10;
bool eq(double a,double b){return abs(a-b)<EPS;}
struct Point{
	double x,y;
	Point(double x_=0,double y_=0):x(x_),y(y_){}
	Point operator-()const{return Point(-x,-y);}
	Point operator+(const Point&p)const{return Point(x+p.x,y+p.y);}
	Point operator-(const Point&p)const{return Point(x-p.x,y-p.y);}
	Point operator*(const double k)const{return Point(x*k,y*k);}
	Point operator/(const double k)const{return Point(x/k,y/k);}
	bool operator<(const Point&p)const{return eq(x,p.x)?y<p.y:x<p.x;}
	bool operator==(const Point&p)const{return eq(x,p.x)&&eq(y,p.y);}
};
istream&operator>>(istream&is,Point&p){return is>>p.x>>p.y;}
ostream&operator<<(ostream&os,const Point&p){return os<<fixed<<setprecision(9)<<p.x<<' '<<p.y;}
struct Line{
	Point p1,p2;
	Line(Point p1_=Point(),Point p2_=Point()):p1(p1_),p2(p2_){}
};
struct Segment:Line{
	Segment(Point p1_=Point(),Point p2_=Point()):Line(p1_,p2_){}
};
struct Circle{
	Point o;
	double r;
	Circle(Point o_=Point(),double r_=0):o(o_),r(r_){}
};
using Polygon=vector<Point>;
//function list begin
Point vec(const Line&);
double norm(const Point&);
double norm(const Line&);
double abs(const Point&);
double abs(const Line&);
double arg(const Point&);
double arg(const Line&);
double arg(const Point&,const Point&,const Point&);//a->b->c
int argtype(const Point&);//(-pi,0]->0,(0,pi]->1
bool argless(const Point&,const Point&);//sorting points with arg
double dot(const Point&,const Point&);
double cross(const Point&,const Point&);
Point polar(const double,const double);
Point rotate(const Point&,const double);
enum{ONLINE_FRONT=-2,CLOCKWISE=-1,ON_SEGMENT=0,COUNTER_CLOCKWISE=1,ONLINE_BACK=2};
int ccw(const Point&,const Point&);
int ccw(const Point&,const Point&,const Point&);
int ccw(const Line&,const Point&);
bool orthogonal(const Point&,const Point&);
bool orthogonal(const Line&,const Line&);
bool parallel(const Point&,const Point&);
bool parallel(const Line&,const Line&);
bool intersect(const Line&,const Point&);
bool intersect(const Line&,const Line&);
bool intersect(const Segment&,const Point&);
bool intersect(const Segment&,const Segment&);
bool intersect(const Line&,const Segment&);
bool intersect(const Segment&,const Line&);
bool intersect(const Circle&,const Point&);
int intersect(const Circle&,const Line&);//count contacts
int intersect(const Circle&,const Segment&);
int intersect(const Circle&,const Circle&);//count common tangents
double distance(const Point&,const Point&);
double distance(const Line&,const Point&);
double distance(const Line&,const Line&);
double distance(const Segment&,const Point&);
double distance(const Segment&,const Segment&);
double distance(const Line&,const Segment&);
double distance(const Segment&,const Line&);
double distance(const Circle&,const Point&);
double distance(const Circle&,const Line&);
double distance(const Circle&,const Segment&);
double distance(const Circle&,const Circle&);
Point projection(const Line&,const Point&);
Point reflection(const Line&,const Point&);
Point crosspoint(const Line&,const Line&);
pair<Point,Point>crosspoint(const Circle&,const Line&);
pair<Point,Point>crosspoint(const Circle&,const Segment&);
pair<Point,Point>crosspoint(const Circle&,const Circle&);
pair<Point,Point>tangent(const Circle&,const Point&);
vector<Line>tangent(const Circle&,const Circle&);
bool is_convex(const Polygon&);
Polygon convex_hull(Polygon,bool=false);
enum{OUT,ON,IN};
int contain(const Polygon&,const Point&);
int contain(const Circle&,const Point&);
int contain(const Circle&,const Segment&);
Polygon convex_cut(const Polygon&,const Line&);
double diameter(Polygon);
double area(const Polygon&);
double area(const Polygon&,const Line&);
double area(const Polygon&,const Circle&);
//function list end
Point vec(const Line&s){return s.p2-s.p1;}
double norm(const Point&p){return p.x*p.x+p.y*p.y;}
double norm(const Line&s){return norm(vec(s));}
double abs(const Point&p){return hypot(p.x,p.y);}
double abs(const Line&s){return abs(vec(s));}
double arg(const Point&p){return atan2(p.y,p.x);}
double arg(const Line&s){return arg(vec(s));}
double arg(const Point&a,const Point&b,const Point&c){
	double A=arg(b-a),B=arg(c-b);
	double theta=abs(A-B);
	return min(theta,2*M_PI-theta);
}
int argtype(const Point&a)
{
	return a.y<-EPS?0:a.y>EPS?1:a.x<0?1:0;
}
bool argless(const Point&a,const Point&b)
{
	int at=argtype(a),bt=argtype(b);
	return at!=bt?at<bt:ccw(a,b)>0;
}
double dot(const Point&a,const Point&b){return a.x*b.x+a.y*b.y;}
double cross(const Point&a,const Point&b){return a.x*b.y-a.y*b.x;}
Point polar(const double r,const double theta){return Point(cos(theta),sin(theta))*r;}
Point rotate(const Point&p,const double theta){
	return Point(p.x*cos(theta)-p.y*sin(theta),p.x*sin(theta)+p.y*cos(theta));
}
int ccw(const Point&a,const Point&b)
{
	return cross(a,b)>EPS?COUNTER_CLOCKWISE
		:cross(a,b)<-EPS?CLOCKWISE
		:dot(a,b)<0?ONLINE_BACK
		:norm(a)<norm(b)?ONLINE_FRONT
		:ON_SEGMENT;
}
int ccw(const Point&a,const Point&b,const Point&c){return ccw(b-a,c-a);}
int ccw(const Line&s,const Point&p){return ccw(s.p1,s.p2,p);}
bool orthogonal(const Point&a,const Point&b){return eq(dot(a,b),0);}
bool orthogonal(const Line&s,const Line&t){return orthogonal(vec(s),vec(t));}
bool parallel(const Point&a,const Point&b){return eq(cross(a,b),0);}
bool parallel(const Line&s,const Line&t){return parallel(vec(s),vec(t));}
bool intersect(const Line&s,const Point&p){return eq(cross(vec(s),p-s.p1),0);}
bool intersect(const Line&s,const Line&t){return !parallel(s,t)||intersect(s,t.p1);}
bool intersect(const Segment&s,const Point&p){return ccw(s,p)==ON_SEGMENT;}
bool intersect(const Segment&s,const Segment&t){
	return ccw(s,t.p1)*ccw(s,t.p2)<=0&&ccw(t,s.p1)*ccw(t,s.p2)<=0;
}
bool intersect(const Line&s,const Segment&t){
	return cross(vec(s),t.p1-s.p1)*cross(vec(s),t.p2-s.p1)<EPS;
}
bool intersect(const Segment&s,const Line&t){return intersect(t,s);}
bool intersect(const Circle&c,const Point&p){return eq(distance(c.o,p),c.r);}
int intersect(const Circle&c,const Line&s){
	double d=distance(s,c.o);
	return eq(d,c.r)?1:d<c.r?2:0;
}
int intersect(const Circle&c,const Segment&s){
	Point h=projection(s,c.o);
	double d1=distance(c.o,s.p1),d2=distance(c.o,s.p2);
	return distance(c.o,h)>c.r+EPS?0
		:d1<c.r-EPS&&d2<c.r-EPS?0
		:d1<c.r-EPS&&d2>c.r-EPS||d1>c.r-EPS&&d2<c.r-EPS?1
		:intersect(s,h)?eq(distance(c.o,h),c.r)?1:2
		:0;
}
int intersect(const Circle&a,const Circle&b){
	double d=distance(a.o,b.o);
	return eq(d,a.r+b.r)?3:d>a.r+b.r?4:eq(d,abs(a.r-b.r))?1:d>abs(a.r-b.r)?2:0;
}
double distance(const Point&a,const Point&b){return abs(a-b);}
double distance(const Line&s,const Point&p){return distance(p,projection(s,p));}
double distance(const Line&s,const Line&t){return intersect(s,t)?0:distance(s,t.p1);}
double distance(const Segment&s,const Point&p){
	return distance(p,
		dot(vec(s),p-s.p1)<0?s.p1
		:dot(-vec(s),p-s.p2)<0?s.p2
		:projection(s,p)
	);
}
double distance(const Segment&s,const Segment&t){
	return intersect(s,t)?0:min({
		distance(s,t.p1),distance(s,t.p2),
		distance(t,s.p1),distance(t,s.p2)
	});
}
double distance(const Line&s,const Segment&t){
	return intersect(s,t)?0:min(distance(s,t.p1),distance(s,t.p2));
}
double distance(const Segment&s,const Line&t){return distance(t,s);}
double distance(const Circle&c,const Point&p){return abs(distance(c.o,p)-c.r);}
double distance(const Circle&c,const Line&s){return max(distance(s,c.o)-c.r,0.);}
double distance(const Circle&c,const Segment&s){
	return intersect(c,s)?0
		:contain(c,s)?c.r-max(distance(c.o,s.p1),distance(c.o,s.p2))
		:distance(s,c.o)-c.r;
}
double distance(const Circle&a,const Circle&b){return max(distance(a.o,b.o)-a.r-b.r,0.);}
Point projection(const Line&s,const Point&p){
	return s.p1+vec(s)*dot(p-s.p1,vec(s))/norm(s);
}
Point reflection(const Line&s,const Point&p){return projection(s,p)*2-p;}
Point crosspoint(const Line&s,const Line&t){
	double d1=cross(vec(s),t.p1-s.p1);
	double d2=-cross(vec(s),t.p2-s.p1);
	return t.p1+vec(t)*(d1/(d1+d2));
}
pair<Point,Point>crosspoint(const Circle&c,const Line&s){
	Point h=projection(s,c.o);
	Point e=vec(s)/abs(s)*sqrt(c.r*c.r-norm(h-c.o));
	return minmax(h-e,h+e);
}
pair<Point,Point>crosspoint(const Circle&c,const Segment&s){
	pair<Point,Point>p=crosspoint(c,Line(s));
	return intersect(c,s)==2?p
		:intersect(s,p.first)?make_pair(p.first,p.first)
		:make_pair(p.second,p.second);
}
pair<Point,Point>crosspoint(const Circle&a,const Circle&b){
	double d=distance(a.o,b.o);
	double alpha=acos((a.r*a.r+d*d-b.r*b.r)/(2*a.r*d));
	double theta=arg(b.o-a.o);
	return minmax(a.o+polar(a.r,theta+alpha),a.o+polar(a.r,theta-alpha));
}
pair<Point,Point>tangent(const Circle&c,const Point&p){
	return crosspoint(c,Circle(p,sqrt(norm(c.o-p)-c.r*c.r)));
}
vector<Line>tangent(const Circle&a,const Circle&b){
	vector<Line>ret;
	double g=distance(a.o,b.o);
	if(eq(g,0))return ret;
	Point u=(b.o-a.o)/g;
	Point v=rotate(u,M_PI/2);
	for(int s:{-1,1}){
		double h=(a.r+b.r*s)/g;
		if(eq(h*h,1))ret.emplace_back(a.o+(h>0?u:-u)*a.r,a.o+(h>0?u:-u)*a.r+v);
		else if(1-h*h>0){
			Point U=u*h,V=v*sqrt(1-h*h);
			ret.emplace_back(a.o+(U+V)*a.r,b.o-(U+V)*b.r*s);
			ret.emplace_back(a.o+(U-V)*a.r,b.o-(U-V)*b.r*s);
		}
	}
	return ret;
}
bool is_convex(const Polygon&P){
	for(int i=0;i<P.size();i++)
		if(ccw(P[i],P[(i+1)%P.size()],P[(i+2)%P.size()])==CLOCKWISE)return false;
	return true;
}
Polygon convex_hull(Polygon P,bool ONSEG){
	if(P.size()<=2)return P;
	sort(P.begin(),P.end());
	Polygon ret(2*P.size());
	int k=0,t;
	if(ONSEG){
		for(const Point&p:P){
			while(k>=2&&ccw(ret[k-2],ret[k-1],p)==CLOCKWISE)k--;
			ret[k++]=p;
		}
		t=k;
		for(int i=P.size()-2;i>=0;i--){
			while(k>=t+1&&ccw(ret[k-2],ret[k-1],P[i])==CLOCKWISE)k--;
			ret[k++]=P[i];
		}
	}
	else{
		for(const Point&p:P){
			while(k>=2&&ccw(ret[k-2],ret[k-1],p)!=COUNTER_CLOCKWISE)k--;
			ret[k++]=p;
		}
		t=k;
		for(int i=P.size()-2;i>=0;i--){
			while(k>=t+1&&ccw(ret[k-2],ret[k-1],P[i])!=COUNTER_CLOCKWISE)k--;
			ret[k++]=P[i];
		}
	}
	ret.resize(k-1);
	int mi=0;
	for(int i=1;i<k-1;i++)
		if(eq(ret[mi].y,ret[i].y)?ret[mi].x>ret[i].x:ret[mi].y>ret[i].y)mi=i;
	rotate(ret.begin(),ret.begin()+mi,ret.end());
	return ret;
}
int contain(const Polygon&P,const Point&p){
	bool in=false;
	for(int i=0;i<P.size();i++){
		Segment s(P[i],P[(i+1)%P.size()]);
		if(intersect(s,p))return ON;
		else{
			Point a=s.p1-p,b=s.p2-p;
			if(a.y>b.y)swap(a,b);
			if(a.y<EPS&&EPS<b.y&&cross(a,b)>EPS)in=!in;
		}
	}
	return in?IN:OUT;
}
int contain(const Circle&c,const Point&p){
	double d=distance(c.o,p);
	return eq(d,c.r)?ON:d<c.r?IN:OUT;
}
int contain(const Circle&c,const Segment&s){
	double d1=distance(c.o,s.p1),d2=distance(c.o,s.p2);
	return d1<c.r+EPS&&d2<c.r+EPS?eq(d1,c.r)||eq(d2,c.r)?ON:IN:OUT;
}
Polygon convex_cut(const Polygon&P,const Line&s){
	Polygon ret;
	for(int i=0;i<P.size();i++){
		Segment t(P[i],P[(i+1)%P.size()]);
		if(ccw(s,t.p1)!=CLOCKWISE)ret.push_back(t.p1);
		if(!parallel(s,t)&&!intersect(s,t.p1)
			&&!intersect(s,t.p2)&&intersect(s,t))ret.push_back(crosspoint(s,t));
	}
	return ret;
}
double diameter(Polygon P){
	if(!is_convex(P))P=convex_hull(P);
	int mi=0,Mi=0;
	for(int i=1;i<P.size();i++){
		if(P[i].x<P[mi].x)mi=i;
		if(P[i].x>P[Mi].x)Mi=i;
	}
	double ret=0;
	int sm=mi,sM=Mi;
	while(mi!=sM||Mi!=sm){
		ret=max(ret,norm(P[mi]-P[Mi]));
		if(cross(P[(mi+1)%P.size()]-P[mi],P[(Mi+1)%P.size()]-P[Mi])<0)mi=(mi+1)%P.size();
		else Mi=(Mi+1)%P.size();
	}
	return sqrt(ret);
}
double area(const Polygon&P){
	double ret=0;
	for(int i=0;i<P.size();i++)ret+=cross(P[i],P[(i+1)%P.size()]);
	return ret/2;
}
double area(const Polygon&P,const Line&s){return area(convex_cut(P,s));}
double area(const Polygon&P,const Circle&c){
	double ret=0;
	for(int i=0;i<P.size();i++)
	{
		Segment s(P[i],P[(i+1)%P.size()]);
		if(contain(c,s))ret+=cross(s.p1-c.o,s.p2-c.o);
		else if(!intersect(c,s)){
			double a=arg(s.p2-c.o)-arg(s.p1-c.o);
			if(a>M_PI)a-=2*M_PI;
			if(a<-M_PI)a+=2*M_PI;
			ret+=c.r*c.r*a;
		}
		else
		{
			pair<Point,Point>p=crosspoint(c,s);
			Point tmp[4]={s.p1,p.first,p.second,s.p2};
			if(intersect(c,Segment(s.p1,p.first))==2)swap(tmp[1],tmp[2]);
			for(int j=0;j<3;j++)
			{
				Segment t(tmp[j],tmp[j+1]);
				if(contain(c,t))ret+=cross(t.p1-c.o,t.p2-c.o);
				else{
					double a=arg(t.p2-c.o)-arg(t.p1-c.o);
					if(a>M_PI)a-=2*M_PI;
					if(a<-M_PI)a+=2*M_PI;
					ret+=c.r*c.r*a;
				}
			}
		}
	}
	return ret/2;
}
#line 4 "a.cpp"
int N;
main()
{
	while(cin>>N,N)
	{
		for(;N--;)
		{
			int M;
			cin>>M;
			vector<Polygon>A(M);
			for(int i=0;i<M;i++)
			{
				for(int j=0;j<4;j++)
				{
					Point p;cin>>p;A[i].push_back(p);
				}
			}
			UF uf(M);
			int ans=M;
			for(int i=0;i<M;i++)for(int j=0;j<M;j++)if(i!=j)
			{
				for(int k=0;k<4;k++)
				{
					if(contain(A[i],A[j][k]))ans-=uf.unite(i,j);
					for(int l=0;l<4;l++)
						if(intersect(Segment(A[i][k],A[i][(k+1)%4]),Segment(A[j][l],A[j][(l+1)%4])))
							ans-=uf.unite(i,j);
				}
			}
			cout<<ans<<endl;
		}
	}
}
accuracy: 1
time_ms: 10
memory_kb: 3200
final_rank: 7

code_snippet: #include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <numeric>
#include <bitset>
#include <cmath>

static const int MOD = 1000000007;
using ll = long long;
using u32 = unsigned;
using u64 = unsigned long long;
using namespace std;

template<class T> constexpr T INF = ::numeric_limits<T>::max()/32*15+208;

using real = double;
static constexpr real EPS = 1e-10;
struct Point {
    real x, y;
    Point& operator+=(const Point a) { x += a.x; y += a.y;  return *this; }
    Point& operator-=(const Point a) { x -= a.x; y -= a.y;  return *this; }
    Point& operator*=(const real k) { x *= k; y *= k;  return *this; }
    Point& operator/=(const real k) { x /= k; y /= k;  return *this; }
    Point operator+(const Point a) const {return Point(*this) += a; }
    Point operator-(const Point a) const {return Point(*this) -= a; }
    Point operator*(const real k) const {return Point(*this) *= k; }
    Point operator/(const real k) const {return Point(*this) /= k; }
    bool operator<(const Point &a) const { return (x != a.x ? x < a.x : y < a.y); }
    explicit Point(real a = 0, real b = 0) : x(a), y(b) {};
};

bool sorty(Point a, Point b){
    return (a.y != b.y ? a.y < b.y : a.x < b.x);
}

istream& operator>> (istream& s, Point& P){
    s >> P.x >> P.y;
    return s;
}

inline real dot(Point a, Point b){ return a.x*b.x + a.y*b.y; }
inline real cross(Point a, Point b){ return a.x*b.y - a.y*b.x; }
inline real abs(Point a){ return sqrt(dot(a, a)); }


real angle(Point A, Point B){
    return acos(dot(A, B)/abs(A)/abs(B));
}



static constexpr int COUNTER_CLOCKWISE = 1;
static constexpr int CLOCKWISE = -1;
static constexpr int ONLINE_BACK = 2;
static constexpr int ONLINE_FRONT = -2;
static constexpr int ON_SEGMENT = 0;

int ccw(Point a, Point b, Point c){
    b -= a; c -= a;
    if(cross(b, c) > EPS) return COUNTER_CLOCKWISE;
    if(cross(b, c) < -EPS) return CLOCKWISE;
    if(dot(b, c) < 0) return ONLINE_BACK;
    if(abs(b) < abs(c)) return ONLINE_FRONT;
    return ON_SEGMENT;
}
struct Segment {
    Point a, b;
    Segment(Point x, Point y) : a(x), b(y) {};
};

struct Line {
    Point a, b;
    Line(Point x, Point y) : a(x), b(y) {};
};

struct Circle{
    Point c; real r;
    Circle(Point c, real r): c(c), r(r) {};
};

using Polygon = vector<Point>;

bool intersect(Segment s, Segment t){
    return (ccw(s.a, s.b, t.a)*ccw(s.a, s.b, t.b) <= 0 &&
            ccw(t.a, t.b, s.a)*ccw(t.a, t.b, s.b) <= 0);
}

bool intersect(Segment s, Line t){
    int a = ccw(t.a, t.b, s.a), b = ccw(t.a, t.b, s.b);
    return (!(a&1) || !(b&1) || a != b);
}

Point polar(double r, double t){
    return Point(r*cos(t), r*sin(t));
}

double arg(Point p){
    return atan2(p.y, p.x);
}

static constexpr int CONTAIN = 0;
static constexpr int INSCRIBE = 1;
static constexpr int INTERSECT = 2;
static constexpr int CIRCUMSCRIBED = 3;
static constexpr int SEPARATE = 4;

int intersect(Circle c1, Circle c2){
    if(c1.r < c2.r) swap(c1, c2);
    real d = abs(c1.c-c2.c);
    real r = c1.r + c2.r;
    if(fabs(d-r) < EPS) return CIRCUMSCRIBED;
    if(d > r) return SEPARATE;
    if(fabs(d+c2.r-c1.r) < EPS) return INSCRIBE;
    if(d+c2.r < c1.r) return CONTAIN;
    return INTERSECT;
}

real distance(Line l, Point c){
    return abs(cross(l.b-l.a, c-l.a)/abs(l.b-l.a));
}


real distance(Segment s, Point c){
    if(dot(s.b-s.a, c-s.a) < EPS) return abs(c-s.a);
    if(dot(s.a-s.b, c-s.b) < EPS) return abs(c-s.b);
    return abs(cross(s.b-s.a, c-s.a)) / abs(s.a-s.b);
}

real distance(Segment s, Segment t){
    if(intersect(s, t)) return 0.0;
    return min({distance(s, t.a), distance(s, t.b),
                distance(t, s.a), distance(t, s.b)});
}


Point project(Line l, Point p){
    Point Q = l.b-l.a;
    return l.a + Q*(dot(p-l.a, Q) / dot(Q, Q));
}


Point project(Segment s, Point p){
    Point Q = s.b-s.a;
    return s.a + Q*(dot(p-s.a, Q) / dot(Q, Q));
}

Point refrect(Segment s, Point p){
    Point Q = project(s, p);
    return Q*2-p;
}

bool isOrthogonal(Segment s, Segment t){
    return fabs(dot(s.b-s.a, t.b-t.a)) < EPS;
}

bool isparallel(Segment s, Segment t){
    return fabs(cross(s.b-s.a, t.b-t.a)) < EPS;
}


Point crossPoint(Segment s, Segment t){
    real d1 = cross(s.b-s.a, t.b-t.a);
    real d2 = cross(s.b-s.a, s.b-t.a);
    if(fabs(d1) < EPS && fabs(d2) < EPS) return t.a;
    return t.a+(t.b-t.a)*d2/d1;
}

Point crossPoint(Line s, Line t){
    real d1 = cross(s.b-s.a, t.b-t.a);
    real d2 = cross(s.b-s.a, s.b-t.a);
    if(fabs(d1) < EPS && fabs(d2) < EPS) return t.a;
    return t.a+(t.b-t.a)*d2/d1;
}

Polygon crossPoint(Circle c, Line l){
    Point p = project(l, c.c), q = (l.b-l.a)/abs(l.b-l.a);
    if(abs(distance(l, c.c)-c.r) < EPS){
        return {p};
    }
    double k = sqrt(c.r*c.r-dot(p-c.c, p-c.c));
    return {p-q*k, p+q*k};
}

Polygon crossPoint(Circle c, Segment s){
    auto tmp = crossPoint(c, Line(s.a, s.b));
    Polygon ret;
    for (auto &&i : tmp) {
        if(distance(s, i) < EPS) ret.emplace_back(i);
    }
    return ret;
}


Polygon crossPoint(Circle c1, Circle c2){
    double d = abs(c1.c-c2.c);
    double a = acos((c1.r*c1.r+d*d-c2.r*c2.r)/(2*c1.r*d));
    double t = arg(c2.c-c1.c);
    return {c1.c+polar(c1.r, t+a), c1.c+polar(c1.r, t-a)};
}

Polygon tangent(Circle c1, Point p){
    Circle c2 = Circle(p, sqrt(dot(c1.c-p, c1.c-p)-c1.r*c1.r));
    return crossPoint(c1, c2);
}
vector<Line> tangent(Circle c1, Circle c2){
    vector<Line> ret;
    if(c1.r < c2.r) swap(c1, c2);
    double k = dot(c1.c-c2.c, c1.c-c2.c);
    if(abs(k) < EPS) return {};
    Point u = (c2.c-c1.c)/sqrt(k);
    Point v(-u.y, u.x);
    for (auto &&i : {-1, 1}) {
        double h = (c1.r+i*c2.r)/sqrt(k);
        if(abs(h*h-1) < EPS){
            ret.emplace_back(c1.c+u*c1.r, c1.c+(u+v)*c1.r);
        }else if(h*h < 1){
            Point u2 = u*h, v2 = v*sqrt(1-h*h);
            ret.emplace_back(c1.c+(u2+v2)*c1.r, c2.c-(u2+v2)*c2.r*i);
            ret.emplace_back(c1.c+(u2-v2)*c1.r, c2.c-(u2-v2)*c2.r*i);
        }
    }
    return ret;
}

real area(Polygon v){
    if(v.size() < 3) return 0.0;
    real ans = 0.0;
    for (int i = 0; i < v.size(); ++i) {
        ans += cross(v[i], v[(i+1)%v.size()]);
    }
    return ans/2;
}

real area(Circle c, Polygon &v){
    int n = v.size();
    real ans = 0.0;
    Polygon u;
    for (int i = 0; i < n; ++i) {
        u.emplace_back(v[i]);
        auto q = crossPoint(c, Segment(v[i], v[(i+1)%n]));
        for (auto &&j : q) {
            u.emplace_back(j);
        }
    }
    for (int i = 0; i < u.size(); ++i) {
        Point A = u[i]-c.c, B = u[(i+1)%u.size()]-c.c;
        if(abs(A) >= c.r+EPS || abs(B) >= c.r+EPS){
            Point C = polar(1, arg(B)-arg(A));
            ans += c.r*c.r*arg(C)/2;
        }else {
            ans += cross(A, B)/2;
        }
    }
    return ans;
}

Polygon convex_hull(Polygon v){
    int n = v.size();
    sort(v.begin(),v.end(), sorty);
    int k = 0;
    Polygon ret(n*2);
    for (int i = 0; i < n; ++i) {
        while(k > 1 && cross(ret[k-1]-ret[k-2], v[i]-ret[k-1]) < 0) k--;
        ret[k++] = v[i];
    }
    for(int i = n-2, t=k; i >= 0; i--){
        while(k > t && cross(ret[k-1]-ret[k-2], v[i]-ret[k-1]) < 0) k--;
        ret[k++] = v[i];
    }
    ret.resize(k-1);
    return ret;
}

bool isconvex(Polygon v){
    int n = v.size();
    for (int i = 0; i < n; ++i) {
        if(ccw(v[(i+n-1)%n], v[i], v[(i+1)%n]) == CLOCKWISE) return false;
    }
    return true;
}

int contains(Polygon v, Point p){
    int n = v.size();
    bool x = false;
    static constexpr int IN = 2;
    static constexpr int ON = 1;
    static constexpr int OUT = 0;
    for (int i = 0; i < n; ++i) {
        Point a = v[i]-p, b = v[(i+1)%n]-p;
        if(fabs(cross(a, b)) < EPS && dot(a, b) < EPS) return ON;
        if(a.y > b.y) swap(a, b);
        if(a.y < EPS && EPS < b.y && cross(a, b) > EPS) x = !x;
    }
    return (x?IN:OUT);
}

real diameter(Polygon v){
    int n = v.size();
    if(n == 2) return abs(v[0]-v[1]);
    int i = 0, j = 0;
    for (int k = 0; k < n; ++k) {
        if(v[i] < v[k]) i = k;
        if(!(v[j] < v[k])) j = k;
    }
    real ret = 0;
    int si = i, sj = j;
    while(i != sj || j != si){
        ret = max(ret, abs(v[i]-v[j]));
        if(cross(v[(i+1)%n]-v[i], v[(j+1)%n]-v[j]) < 0.0) i = (i+1)%n;
        else j = (j+1)%n;
    }
    return ret;
}

Polygon convexCut(Polygon v, Line l){
    Polygon q;
    int n = v.size();
    for (int i = 0; i < n; ++i) {
        Point a = v[i], b = v[(i+1)%n];
        if(ccw(l.a, l.b, a) != -1) q.push_back(a);
        if(ccw(l.a, l.b, a)*ccw(l.a, l.b, b) < 0){
            q.push_back(crossPoint(Line(a, b), l));
        }
    }
    return q;
}

real closest_pair(Polygon &v, int l = 0, int r = -1){
    if(!(~r)){
        r = v.size();
        sort(v.begin(),v.end());
    }
    if(r - l < 2) {
        return abs(v.front()-v.back());
    }
    int mid = (l+r)/2;
    real p = v[mid].x;
    real d = min(closest_pair(v, l, mid), closest_pair(v, mid, r));
    inplace_merge(v.begin()+l, v.begin()+mid, v.begin()+r, sorty);
    Polygon u;
    for (int i = l; i < r; ++i) {
        if(fabs(v[i].x-p) >= d) continue;
        for (int j = 0; j < u.size(); ++j) {
            real dy = v[i].y-next(u.rbegin(), j)->y;
            if(dy >= d) break;
            d = min(d, abs(v[i]-*next(u.rbegin(), j)));
        }
        u.emplace_back(v[i]);
    }
    return d;
}

class UnionFind {
    int n;
    vector<int> uni;
    int forest_size;
public:
    explicit UnionFind(int n) : n(n), uni(static_cast<u32>(n), -1), forest_size(n) {};

    int root(int a){
        if (uni[a] < 0) return a;
        else return (uni[a] = root(uni[a]));
    }

    bool unite(int a, int b) {
        a = root(a);
        b = root(b);
        if(a == b) return false;
        if(uni[a] > uni[b]) swap(a, b);
        uni[a] += uni[b];
        uni[b] = a;
        forest_size--;
        return true;
    }
    int size(){ return forest_size; }
    int size(int i){ return -uni[root(i)]; }
    bool same(int a, int b) { return root(a) == root(b); }
};

bool solve(){
    int t;
    cin >> t;
    if(!t) return 0;
    while(t--){
        int n;
        cin >> n;
        UnionFind uf(n);
        vector<Polygon> v(n, Polygon(4));
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < 4; ++j) {
                cin >> v[i][j];
            }
        }
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                for (int k = 0; k < 4; ++k) {
                    if(contains(v[i], v[j][k])) {
                        uf.unite(i, j);
                        break;
                    }
                    for (int l = 0; l < 4; ++l) {
                        if(intersect(Segment(v[i][k], v[i][(k+1)&3]), Segment(v[j][l], v[j][(l+1)&3]))) {
                            uf.unite(i, j);
                            k = l = 4;
                        }
                    }
                }
            }
        }
        cout << uf.size() << "\n";
    }
    return 1;
}

int main() {
    while(solve());

    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3100
final_rank: 5

code_snippet: #include<iostream>
#include<vector>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<cassert>
using namespace std;

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

struct Point;
typedef Point Vector;
typedef vector<Point> Polygon;
struct Circle;
struct Segment;
typedef Segment Line;
double norm(Point a);
double abs(Point a);
double dot(Vector a, Vector b);
double cross(Vector a, Vector b);
double getDistance(Point a, Point b);
double getDistanceLP(Line l, Point p);
double getDistanceSP(Segment s, Point p);
double getDistance(Segment s1, Segment s2);
bool isOrthogonal(Vector a, Vector b);
bool isOrthogonal(Point a1, Point a2, Point b1, Point b2);
bool isOrthogonal(Segment s1, Segment s2);
bool isParallel(Vector a, Vector b);
bool isParallel(Point a1, Point a2, Point b1, Point b2);
bool isParallel(Segment s1, Segment s2);
int ccw(Point p0, Point p1, Point p2);
bool intersect(Point p1, Point p2, Point p3, Point p4);
bool intersect(Segment s1, Segment s2);
bool intersect(Circle c, Line l); // 誤差の検証をしていない
bool intersect(Circle c1, Circle c2); // 誤差の検証をしていない

Point project(Segment s, Point p);
Point reflect(Segment s, Point p);
Point getCrossPoint(Segment s1, Segment s2);
pair<Point,Point> getCrossPoints(Circle c, Line l);
pair<Point,Point> getCrossPoints(Circle c1, Circle c2); // 誤差の検証をしていない
pair<Point,Point> getContactPoints(Circle c, Point p); // 接点 点は円の外部

double area(Polygon g); // convexでなくてもよい. absを消せば符号付き面積
bool isConvex(Polygon g); // O(n^2) 線形時間アルゴリズムが存在するらしい
int contains(Polygon g, Point p);

double arg(Vector p);   // 偏角
Vector polar(double a, double r); // 極座標系->ベクトル

Polygon andrewScan(Polygon g); // 凸包の辺上の点も含めたければ!=CLOCKWISEを==COUNTER_CLOCKWISEに
double convexDiameter(Polygon g); // gはconvex 


struct Point{
    double x, y;

    Point(double x = 0, double y = 0) : x(x), y(y) {}

    Point operator + (Point p){ return Point(x+p.x, y+p.y); }
    Point operator - (Point p){ return Point(x-p.x, y-p.y); }
    Point operator * (double a){ return Point(a*x, a*y); }
    Point operator / (double a){ return Point(x/a, y/a); }

    double abs() { return sqrt(norm()); }
    double norm() { return x*x + y*y; }

    bool operator < (const Point &p) const{
        return x != p.x ? x < p.x : y < p.y;
    }

    bool operator == (const Point &p) const{
        return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
    }
};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Circle{
    Point c;
    double r;
    Circle(Point c = Point(), double r = 0.0) : c(c), r(r) {}
};

struct Segment{
    Point p1, p2;
    Segment(Point p1, Point p2) : p1(p1), p2(p2) {}
};

typedef Segment Line;


double norm(Point a){
    return a.x * a.x + a.y * a.y;
}

double abs(Point a){
    return sqrt(norm(a));
}

double dot(Vector a, Vector b){
    return a.x * b.x + a.y * b.y;
}

double cross(Vector a, Vector b){
    return a.x * b.y - a.y * b.x;
}

double getDistance(Point a, Point b){
    return abs(a - b);
}

double getDistanceLP(Line l, Point p){
    return abs(cross(l.p2 - l.p1, p - l.p1) / abs(l.p2 - l.p1));
}

double getDistanceSP(Segment s, Point p){
    if(dot(s.p2-s.p1, p-s.p1) < 0.0)    return abs(p-s.p1);
    if(dot(s.p1-s.p2, p-s.p2) < 0.0)    return abs(p-s.p2);
    return getDistanceLP(s, p);
}

double getDistance(Segment s1, Segment s2){
    if(intersect(s1, s2))   return 0.0;
    return min({getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2), 
                getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2)});
}


bool isOrthogonal(Vector a, Vector b){
    return equals(dot(a, b), 0.0);
}
bool isOrthogonal(Point a1, Point a2, Point b1, Point b2){
    return isOrthogonal(a1-a2, b1-b2);
}
bool isOrthogonal(Segment s1, Segment s2){
    return equals(dot(s1.p2-s1.p1, s2.p2-s2.p1), 0.0);
}
bool isParallel(Vector a, Vector b){
    return equals(cross(a, b), 0.0);
}
bool isParallel(Point a1, Point a2, Point b1, Point b2){
    return isParallel(a1-a2, b1-b2);
}
bool isParallel(Segment s1, Segment s2){
    return equals(cross(s1.p2-s1.p1, s2.p2-s2.p1), 0.0);
}


static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;   // p2->p0->p1
static const int ONLINE_FRONT = -2; // p0->p1->p2
static const int ON_SEGMENT = 0;    // p0->p2->p1
int ccw(Point p0, Point p1, Point p2){
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if(cross(a, b) > EPS)   return COUNTER_CLOCKWISE;
    if(cross(a, b) < -EPS)  return CLOCKWISE;
    if(dot(a, b) < -EPS)    return ONLINE_BACK;
    if(norm(a) < norm(b))   return ONLINE_FRONT;
    return ON_SEGMENT;
}

bool intersect(Point p1, Point p2, Point p3, Point p4){
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 &&
            ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool intersect(Segment s1, Segment s2){
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}
bool intersect(Circle c, Line l){
    return getDistanceLP(l, c.c) < c.r+EPS;
}
bool intersect(Circle c1, Circle c2){
    return abs(c1.r-c2.r) <= getDistance(c1.c, c2.c) && getDistance(c1.c, c2.c) < c1.r+c2.r+EPS;
}

Point project(Segment s, Point p){
    Vector base = s.p2 - s.p1;
    double r = dot(p - s.p1, base) / norm(base);
    return s.p1 + base * r;
}

Point reflect(Segment s, Point p){
    return p + (project(s, p) - p) * 2.0;
}

Point getCrossPoint(Segment s1, Segment s2){
    Vector base = s2.p2 - s2.p1;
    double d1 = abs(cross(base, s1.p1-s2.p1));
    double d2 = abs(cross(base, s1.p2-s2.p1));
    double t = d1 / (d1 + d2);
    return s1.p1 + (s1.p2 - s1.p1) * t;
}

pair<Point,Point> getCrossPoints(Circle c, Line l){
    assert(intersect(c, l));
    Vector pr = project(l, c.c);
    Vector e = (l.p2 - l.p1) / abs(l.p2 - l.p1);
    double base = sqrt(c.r * c.r - norm(pr - c.c));
    return make_pair(pr + e*base, pr - e*base);
}

pair<Point,Point> getCrossPoints(Circle c1, Circle c2){
    assert(intersect(c1, c2));
    double d = abs(c1.c - c2.c);
    double a = acos( (c1.r*c1.r + d*d - c2.r*c2.r)/(2*c1.r*d) );
    double t = arg(c2.c - c1.c);
    return make_pair(c1.c + polar(c1.r, t+a), c1.c + polar(c1.r, t-a));
}

pair<Point,Point> getContactPoints(Circle c, Point p){
    assert(c.r < getDistance(c.c, p));
    double d = getDistance(c.c, p);
    return getCrossPoints(c, Circle(p, sqrt(d*d-c.r*c.r)));
}

double area(Polygon g){
    if(g.size() < 3)    return 0;
    int n = g.size();
    Point o(0.0, 0.0);
    double s = 0.0;
    for(int i = 0; i < n; i++)  s += cross(g[i]-o, g[(i+1)%n]-o);
    return abs(s) / 2.0;
}

bool isConvex(Polygon g){
    bool ret = true;
    int n = g.size();
    for(int i = 0; i < n; i++){
        for(int j = i+1; j < n; j++){
            if(cross(g[i]-g[(i+n-1)%n], g[j]-g[(i+n-1)%n]) < -EPS || cross(g[(i+1)%n]-g[i], g[j]-g[i]) < -EPS){
                ret = false;
            }
        }
    }
    return ret;
}

static const int IN = 2;
static const int ON = 1;
static const int OUT = 0;
int contains(Polygon g, Point p){
    int n = g.size();
    bool x = false;
    for(int i = 0; i < n; i++){
        Point a = g[i] - p, b = g[(i+1)%n] - p;
        if(abs(cross(a, b)) < EPS && dot(a, b) < EPS)   return ON;
        if(a.y > b.y)   swap(a, b);
        if(a.y < EPS && EPS < b.y && cross(a, b) > EPS) x = !x;
    }
    return x ? IN : OUT;
}


double arg(Vector p){
    return atan2(p.y, p.x);
}

Vector polar(double a, double r){
    return Point(a * cos(r), a * sin(r));
}


Polygon andrewScan(Polygon g){
    Polygon u, l;
    if(g.size() < 3)    return g;
    sort(g.begin(), g.end());
    u.push_back(g[0]);
    u.push_back(g[1]);
    l.push_back(g[g.size()-1]);
    l.push_back(g[g.size()-2]);

    // upper
    for(int i = 2; i < g.size(); i++){
        for(int n = u.size(); n >= 2 && ccw(u[n-2], u[n-1], g[i]) != CLOCKWISE; n--){
            u.pop_back();
        }
        u.push_back(g[i]);
    }

    // lower
    for(int i = g.size()-3; i >= 0; i--){
        for(int n = l.size(); n >= 2 && ccw(l[n-2], l[n-1], g[i]) != CLOCKWISE; n--){
            l.pop_back();
        }
        l.push_back(g[i]);
    }

    reverse(l.begin(), l.end());
    for(int i = u.size()-2; i >= 1; i--)    l.push_back(u[i]);

    return l;
}

double convexDiameter(Polygon g){
    double d = 0.0;
    int n = g.size();
    int is = 0, js = 0;
    for(int i = 1; i < n; i++){
        if(g[i].y > g[is].y)    is = i;
        if(g[i].y < g[js].y)    js = i;
    }
    d = getDistance(g[is], g[js]);

    int i = is, j = js, maxi = is, maxj = js;
    do{
        if(cross(g[(i+1)%n]-g[i], g[(j+1)%n]-g[j]) >= 0.0)  j = (j+1)%n;
        else    i = (i+1)%n;
        if(getDistance(g[i], g[j]) > d){
            d = getDistance(g[i], g[j]);
            maxi = i, maxj = j;
        }
    }while(i != is || j != js);

    return d; // farthest pair is (maxi, maxj).
}

struct UF{
    vector<int> p;
    int n;

    UF(int siz){
        n = siz;
        p.resize(n, 0);
        for(int i = 0; i < n; i++)  p[i] = i;
    }

    int parent(int x){
        if(p[x] != x)   p[x] = parent(p[x]);
        return p[x];
    }

    void unite(int x, int y){
        x = parent(x), y = parent(y);
        p[x] = y;
    }
};

int main(){
    int q;
    while(cin >> q, q){
        while(q-- > 0){
            int n;
            cin >> n;
            Polygon g[n];
            for(int i = 0; i < n; i++){
                for(int j = 0; j < 4; j++){
                    double x, y;
                    cin >> x >> y;
                    g[i].push_back(Point(x,y));
                }
            }
            UF uf(n);
            for(int i = 0; i < n; i++){
                for(int j = 0; j < n; j++){
                    bool judge = true;
                    for(int k = 0; k < 4; k++){
                        judge &= contains(g[j], g[i][k])!=0;
                    }
                    for(int k = 0; k < 4; k++){
                        for(int l = 0; l < 4; l++){
                            judge |= intersect(g[i][k], g[i][(k+1)%4], g[j][l], g[j][(l+1)%4]);
                            if(isParallel(g[i][k], g[i][(k+1)%4], g[j][l], g[j][(l+1)%4])){
                                if(ccw(g[j][l], g[j][(l+1)%4], g[i][k]) == ON_SEGMENT)  judge = true;
                                if(ccw(g[j][l], g[j][(l+1)%4], g[i][(k+1)%4]) == ON_SEGMENT)  judge = true;
                            }
                        }
                    }
                    if(judge)   uf.unite(i, j);
                }
            }
            vector<int> ans;
            for(int i = 0; i < n; i++)  ans.push_back(uf.parent(i));
            sort(ans.begin(), ans.end());
            ans.erase(unique(ans.begin(),ans.end()), ans.end());
            cout << ans.size() << endl;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3248
final_rank: 12

code_snippet: #include <iostream>
#include <vector>
#include <algorithm>
#include <complex>
using namespace std;

// xy平面上の点(ベクトル)を表現するには、complex型を利用するとよい
typedef complex<double> P;

// 辺の表現 (座標を2つ pair でもつ)
typedef pair<P, P> L;

// 円の表現 (座標 P と 半径 d で表現する)
typedef pair<P, double> C;

// 成分を取り出すのを簡単にする
#define X real()
#define Y imag()

// 誤差(epsilon)の定義
#define EPS (1e-10)

// 2つの要素が等しいかどうか
#define EQ(a,b) (abs((a) - (b)) < EPS)

// 2つのベクトルが等しいかどうか
#define EQV(a,b) ( EQ((a).X, (b).X) && EQ((a).Y, (b).Y) )

// m は n より大きい(以上)かどうか
#define LE(n, m) ((n) < (m) + EPS)
#define LEQ(n, m) ((n) <= (m) + EPS)

// m は n より小さい(以下)かどうか
#define GE(n, m) ((n) + EPS > (m))
#define GEQ(n, m) ((n) + EPS >= (m))

// 2つのベクトルの内積を求める
double dot(P a, P b) {
    return (a.X * b.X + a.Y * b.Y);
}

// 2つのベクトルの外積を求める
double cross(P a, P b) {
    return (a.X * b.Y - a.Y * b.X);
}

// Union-Find 木 (Verified: AtCoder Typical Contest 001 B)
struct UnionFind {
private:
    const int n;
    int __size;
    vector<int> uf;
public:
    // 初期化 UnionFind uni(n) のように宣言すれば良い
    UnionFind(int _n) : n(_n), __size(_n), uf(_n, -1) {}
    // find (木の根を求める)
    int find(int x) {return (uf[x] < 0) ? x : uf[x] = find(uf[x]);}
    // x と y が同じ集合に属するかどうか
    bool same(int x, int y) {return find(x) == find(y);}
    // x が属する集合の要素数
    int size(int x) {return -uf[find(x)];}
    // 集合はいくつあるか
    int size()      {return __size;}
    // x と y の属する集合を併合
    void unite(int x, int y) {
        x = find(x); y = find(y);
        if(x == y) return;
        __size--;
        if(uf[y] < uf[x]) swap(x, y);
        uf[x] += uf[y]; uf[y] = x;
    }
};

// 凸多角形の内部判定 O(log n)
// 点が領域内部なら1、境界上なら2、外部なら0を返す
int inCcwConvex(P p, const vector<P> &ps) {
    int n = ps.size();
    P g = (ps[0] + ps[n / 3] + ps[n*2 / 3]) / 3.0;
    if(g == p) return 1;
    P gp = p - g;

    int l = 0, r = n;
    while(l + 1 < r) {
        int mid = (l + r) / 2;
        P gl = ps[l] - g;
        P gm = ps[mid] - g;
        if(cross(gl,gm) > 0) {
            if(cross(gl,gp) >= 0 && cross(gm,gp) <= 0) r = mid;
            else l = mid;
        }
        else {
            if(cross(gl,gp) <= 0 && cross(gm,gp) >= 0) l = mid;
            else r = mid;
        }
    }
    r %= n;
    double cr = cross(ps[l] - p, ps[r] - p);
    return EQ(cr, 0) ? 2 : cr < 0 ? 0 : 1;
}

// ccw (c が直線(線分) ab に対してどのような位置関係か？)
// Verified: AOJ CGL_1_C: Counter-Clockwise
// +1 ... a → b で半時計方向に折れて b → c (COUNTER_CLOCKWISE)
// -1 ... a → b で時計方向に折れて b → c (CLOCKWISE)
// +2 ... c, a, b がこの順で同一直線状にある場合 (ONLINE_BACK)
// -2 ... a, b, c がこの順で同一直線状にある場合 ( or a == b ) (ONLINE_FRONT)
//  0 ... c が線分 ab 上にある場合 (点 a, b 上を含む) (ON_SEGMENT)
int ccw(P a, P b, P c) {
    b -= a; c -= a;
    if( cross(b,c) > EPS ) return +1;
    if( cross(b,c) < -EPS ) return -1;
    if( dot(b,c) < 0 ) return +2;
    if( norm(b) < norm(c) ) return -2;
    return 0;
}

// 線分 a1, a2 と線分 b1, b2 の交差判定
// Verified: CGL_2_B: Intersection
bool isec_ss(P a1, P a2, P b1, P b2) {
    return ( ccw(a1,a2,b1) * ccw(a1,a2,b2) <= 0 ) &&
           ( ccw(b1,b2,a1) * ccw(b1,b2,a2) <= 0 );
}

bool intersect(vector<P> A, vector<P> B) {
    bool res = false;
    for(size_t k=0; k<A.size(); k++) {
        if(inCcwConvex(A[k], B) > 0) {
            res = true;
        }
    }

    for(size_t i=0; i<A.size(); i++) {
        for(size_t j=0; j<B.size(); j++) {
            L la(A[i], A[(i+1)%A.size()]), lb(B[j], B[(j+1)%B.size()]);
            if(isec_ss(la.first, la.second, lb.first, lb.second)) res = true;
        }
    }
    return res;
}

int solve_testcase() {
    int N; cin >> N;
    if(N == 0) return 1;

    for(int i=0; i<N; i++) {
        int M; cin >> M;
        UnionFind uf(M);
        vector< vector<P> > polys;
        for(int j=0; j<M; j++) {
            vector<P> p;
            for(int k=0; k<4; k++) {
                double x, y; cin >> x >> y;
                p.emplace_back(x, y);
            }
            reverse(p.begin(), p.end());
            polys.emplace_back(p);
        }
        for(int x=0; x<M; x++) {
            for(int y=0; y<x; y++) {
                if(intersect(polys[x], polys[y]) or intersect(polys[y], polys[x])) {
                    uf.unite(x, y);
                }
            }
        }
        cout << uf.size() << endl;
    }
    return 0;
}

int main() {
    while(!solve_testcase());
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3204
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

struct UnionFind{
    vector<int> par;
    vector<int> ranks;
    vector<int> size;

private:
    void init(int n = 1){
        par.resize(n);
        ranks.resize(n);
        size.resize(n);
        for(int i=0; i<n; i++){
            par[i] = i;
            ranks[i] = 0;
            size[i] = 1;
        }
    }

public:
    UnionFind(int n = 1){
        init(n);
    }

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

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

    void unite(int x, int y){
        x = find(x);
        y = find(y);
        if(x == y) return ;
        if(ranks[x] < ranks[y]) swap(x, y);
        if(ranks[x] == ranks[y]) ranks[x]++;
        par[y] = x;
        size[x] += size[y];
        return ;
    }
};

struct Point{
    double x, y;
    Point(double x = 0, double y = 0): x(x), y(y) {}
    Point operator + (Point p){
        return Point(x + p.x, y + p.y);
    }
    Point operator - (Point p){
        return Point(x - p.x, y - p.y);
    }
    Point operator * (double a){
        return Point(a * x, a * y);
    }
    Point operator / (double a){
        return Point(x / a, y / a);
    }

    double norm(){
        return x * x + y * y;
    }
    double abs(){
        return sqrt(norm());
    }

    bool operator < (const Point &p) const {
        return (x != p.x ? x < p.x : y < p.y);
    }

};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Segment{
    Point p1, p2;
    Segment(Point p1, Point p2): p1(p1), p2(p2) {}
};

typedef Segment Line;

double dot(Vector a, Vector b){
    return a.x * b.x + a.y * b.y;
}

double cross(Vector a, Vector b){
    return a.x * b.y - a.y * b.x;
}

double norm(Vector a){
    return a.x * a.x + a.y * a.y;
}

double abs(Vector a){
    return sqrt(norm(a));
}

int ccw(Point p0, Point p1, Point p2){
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if(cross(a, b) > EPS) return 1;
    if(cross(a, b) < -EPS) return -1;
    if(dot(a, b) < -EPS) return 2;
    if(a.norm() < b.norm()) return -2;
    return 0;
}

bool intersect(Point p1, Point p2, Point p3, Point p4){
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}

bool intersect(Segment s1, Segment s2){
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}

int contains(Polygon g, Point p){
    int n = g.size();
    bool x = false;
    for(int i=0; i<n; i++){
        Point a = g[i] - p;
        Point b = g[(i+1)%n] - p;
        if(abs(cross(a, b)) < EPS && dot(a, b) < EPS) return 1;
        if(a.y > b.y) swap(a, b);
        if(a.y < EPS && EPS < b.y && cross(a, b) > EPS) x = !x;
    }

    return (x ? 2 : 0);
}

int main(){
    while(1){
        int n; cin>>n;
        if(n == 0) return 0;
        while(n--){
            int m; cin>>m;
            vector<Polygon> square(m);
            for(int i=0; i<m; i++){
                for(int j=0; j<4; j++){
                    double a, b; cin>>a>>b;
                    square[i].emplace_back(a, b);
                }
            }

            UnionFind uf(m);
            for(int i=0; i<m; i++){
                for(int j=0; j<m; j++){
                    if(i == j) continue;

                    for(int k=0; k<4; k++){
                        for(int l=0; l<4; l++){
                            Segment s1 = {square[i][k], square[i][(k+1)%4]};
                            Segment s2 = {square[j][l], square[j][(l+1)%4]};

                            if(intersect(s1, s2)){
                                if(!uf.same(i, j)){
                                    uf.unite(i, j);
                                }
                            }
                        }
                    }

                    if(!uf.same(i, j)){
                        int contain = 1;
                        for(int k=0; k<4; k++){
                            contain *= contains(square[i], square[j][k]);
                        }

                        if(contain != 0){
                            uf.unite(i, j);
                        }

                        contain = 1;
                        for(int k=0; k<4; k++){
                            contain *= contains(square[j], square[i][k]);
                        }
                        
                        if(contain != 0){
                            if(!uf.same(i, j)){
                                uf.unite(i, j);
                            }
                        }
                    }
                }
            }

            set<int> st;
            for(int i=0; i<m; i++){
                st.emplace(uf.find(i));
            }

            cout << st.size() << endl;
        }
    }
}
accuracy: 1
time_ms: 20
memory_kb: 3076
final_rank: 10

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define EPS (1e-10)
#define equals(a, b) (fabs((a) - (b)) < EPS)

const int MAX_N = 110;

int par[MAX_N];
int ranks[MAX_N];
int ufSize;

void init(int n){
    ufSize = n;
    for(int i=0; i<n; i++){
        par[i] = i;
        ranks[i] = 0;
    }
}

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

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

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

    if(ranks[x] < ranks[y]) par[x] = y;
    else par[y] = x;

    if(ranks[x] == ranks[y]){
        ranks[x]++;
    }
}

struct Point{
    double x, y;
    Point(double x = 0, double y = 0): x(x), y(y) {}
    Point operator + (Point p){
        return Point(x + p.x, y + p.y);
    }
    Point operator - (Point p){
        return Point(x - p.x, y - p.y);
    }
    Point operator * (double a){
        return Point(a * x, a * y);
    }
    Point operator / (double a){
        return Point(x / a, y / a);
    }

    double norm(){
        return x * x + y * y;
    }
    double abs(){
        return sqrt(norm());
    }

    bool operator < (const Point &p) const {
        return (x != p.x ? x < p.x : y < p.y);
    }

};

typedef Point Vector;
typedef vector<Point> Polygon;

struct Segment{
    Point p1, p2;
    Segment(Point p1, Point p2): p1(p1), p2(p2) {}
};

typedef Segment Line;

double dot(Vector a, Vector b){
    return a.x * b.x + a.y * b.y;
}

double cross(Vector a, Vector b){
    return a.x * b.y - a.y * b.x;
}

double norm(Vector a){
    return a.x * a.x + a.y * a.y;
}

double abs(Vector a){
    return sqrt(norm(a));
}

int ccw(Point p0, Point p1, Point p2){
    Vector a = p1 - p0;
    Vector b = p2 - p0;
    if(cross(a, b) > EPS) return 1;
    if(cross(a, b) < -EPS) return -1;
    if(dot(a, b) < -EPS) return 2;
    if(a.norm() < b.norm()) return -2;
    return 0;
}

bool intersect(Point p1, Point p2, Point p3, Point p4){
    return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}

bool intersect(Segment s1, Segment s2){
    return intersect(s1.p1, s1.p2, s2.p1, s2.p2);
}

double getDistanceLP(Line l, Point p){
    return abs(cross(l.p2 - l.p1, p - l.p1) / abs(l.p2 - l.p1));
}

double getDistanceSP(Segment s, Point p){
    if(dot(s.p2 - s.p1, p - s.p1) < -EPS) return abs(p - s.p1);
    if(dot(s.p1 - s.p2, p - s.p2) < -EPS) return abs(p - s.p2);
    return getDistanceLP(s, p);
}

double getDistance(Segment s1, Segment s2){
    if(intersect(s1, s2)) return 0;
    return min({getDistanceSP(s1, s2.p1), getDistanceSP(s1, s2.p2), getDistanceSP(s2, s1.p1), getDistanceSP(s2, s1.p2)});
}

int contains(Polygon g, Point p){
    int n = g.size();
    bool x = false;
    for(int i=0; i<n; i++){
        Point a = g[i] - p;
        Point b = g[(i+1)%n] - p;
        if(abs(cross(a, b)) < EPS && dot(a, b) < EPS) return 1;
        if(a.y > b.y) swap(a, b);
        if(a.y < EPS && EPS < b.y && cross(a, b) > EPS) x = !x;
    }

    return (x ? 2 : 0);
}

int main(){
    while(1){
        int n; cin>>n;
        if(n == 0) return 0;
        while(n--){
            int m; cin>>m;
            vector<double> x1(m), x2(m), x3(m), x4(m), y1(m), y2(m), y3(m), y4(m);
            for(int i=0; i<m; i++){
                cin>>x1[i]>>y1[i]>>x2[i]>>y2[i]>>x3[i]>>y3[i]>>x4[i]>>y4[i];
            }

            init(m);
            for(int i=0; i<m; i++){
                for(int j=0; j<m; j++){
                    if(i == j) continue;

                    Segment s1 = {Point(x1[i], y1[i]), Point(x2[i], y2[i])};
                    Segment s2 = {Point(x2[i], y2[i]), Point(x3[i], y3[i])};
                    Segment s3 = {Point(x3[i], y3[i]), Point(x4[i], y4[i])};
                    Segment s4 = {Point(x4[i], y4[i]), Point(x1[i], y1[i])};

                    vector<Segment> S1;
                    S1.emplace_back(s1);
                    S1.emplace_back(s2);
                    S1.emplace_back(s3);
                    S1.emplace_back(s4);

                    Segment s5 = {Point(x1[j], y1[j]), Point(x2[j], y2[j])};
                    Segment s6 = {Point(x2[j], y2[j]), Point(x3[j], y3[j])};
                    Segment s7 = {Point(x3[j], y3[j]), Point(x4[j], y4[j])};
                    Segment s8 = {Point(x4[j], y4[j]), Point(x1[j], y1[j])};
                
                    vector<Segment> S2;
                    S2.emplace_back(s5);
                    S2.emplace_back(s6);
                    S2.emplace_back(s7);
                    S2.emplace_back(s8);

                    for(int k=0; k<4; k++){
                        for(int l=0; l<4; l++){
                            if(equals(getDistance(S1[k], S2[l]), 0)){
                                if(!same(i, j)){
                                    unite(i, j);
                                }
                            }
                        }
                    }

                    if(!same(i, j)){
                        Polygon po1;
                        po1.emplace_back(Point(x1[i], y1[i]));
                        po1.emplace_back(Point(x2[i], y2[i]));
                        po1.emplace_back(Point(x3[i], y3[i]));
                        po1.emplace_back(Point(x4[i], y4[i]));
                        int contain = 1;
                        contain *= contains(po1, Point(x1[j], y1[j]));
                        contain *= contains(po1, Point(x2[j], y2[j]));
                        contain *= contains(po1, Point(x3[j], y3[j]));
                        contain *= contains(po1, Point(x4[j], y4[j]));
                        
                        if(contain != 0){
                            unite(i, j);
                        }

                        Polygon po2;
                        po2.emplace_back(Point(x1[j], y1[j]));
                        po2.emplace_back(Point(x2[j], y2[j]));
                        po2.emplace_back(Point(x3[j], y3[j]));
                        po2.emplace_back(Point(x4[j], y4[j]));
                        contain = 1;
                        contain *= contains(po2, Point(x1[i], y1[i]));
                        contain *= contains(po2, Point(x2[i], y2[i]));
                        contain *= contains(po2, Point(x3[i], y3[i]));
                        contain *= contains(po2, Point(x4[i], y4[i]));
                        

                        if(contain != 0){
                            if(!same(i, j)){
                                unite(i, j);
                            }
                        }
                    }
                }
            }

            cout << ufSize << endl;
        }
    }
}
accuracy: 1
time_ms: 50
memory_kb: 3200
final_rank: 17

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef long long int ll;
typedef pair<ll,ll> mp;
#define inf 1e9
#define EPS 1e-10

struct point{
	double x,y;
	point(){}
	point(double x,double y) :x(x),y(y){}

	point operator - (point p) {return point(x-p.x,y-p.y); }
	void make() { cin>>x>>y;}
	double norm() { return x*x+y*y; }
};

double dot(point a,point b){return a.x*b.x + a.y*b.y; }
double cross(point a,point b){ return a.x*b.y - a.y*b.x ;}

typedef vector<point> polygon;
int contain(polygon g,point p){
	int n = g.size();
	bool f = false;
	for(int i=0;i<n;i++){
		point a = g[i]-p,b = g[(i+1)%n]-p;
		if( fabs(cross(a,b) ) < EPS && dot(a,b) <EPS)return 1;
		if( a.y > b.y )swap(a,b);
		if( a.y < EPS && EPS < b.y && cross(a,b) >EPS) f = !f;
	}
	return (f?2:0);
// iN 2 ON 1OUT 0
}
static const int CCW_COUNTER_CLOCKWISE = 1;
static const int CCW_CLOCKWISE = -1;
static const int CCW_ONLINE_BACK = 2;
static const int CCW_ONLINE_FRONT = -2;
static const int CCW_ON_SEGMENT = 0;
int ccw(point p0,point p1,point p2){
	point a = p1-p0,b = p2-p0;
	if(cross(a,b) > EPS) return 1;
	if(cross(a,b) < -EPS) return -1;
	if(dot(a,b) < -EPS ) return 2;
	if(a.norm() < b.norm() ) return -2;
	return 0;
}
bool intersect(point a,point b,point c,point d){
	return ( ccw(a,b,c)*ccw(a,b,d) <= 0 && ccw(c,d,a)*ccw(c,d,b) <= 0 );
}
struct  disjointset{
	vector<int> rank,p;
	disjointset(){}
	disjointset(int size){
		rank.resize(size,0);
		p.resize(size,0);
		for(int i=0;i<size;i++)makeSet(i);
	}
	void makeSet(int x){
		p[x] = x;
		rank[x] = 0;
	}
	bool same(int x,int y){
		return findSet(x) == findSet(y);
	}
	void unite(int x,int y){
		link(findSet(x),findSet(y) );
	}
	void link(int x,int y){
		if(rank[x] > rank[y]){
			p[y] = x;
		}else{	
			p[x] = y;
			if(rank[x] == rank[y] ) rank[y]++;
		}
	}
	int findSet(int x){
		if(x!=p[x]) p[x] = findSet(p[x]);
		return p[x];
	}

};

int main(){
	while(1){
		int n;
		cin>>n;
		if(n==0)break;
		for(int k=0;k<n;k++){
		    	int m;
			cin>>m;
			vector<polygon> g(m,vector<point>(4,point(0,0)) );
			for(int i=0;i<m;i++)for(int j=0;j<4;j++) g[i][j].make();
		/*	for(int i=0;i<m;i++){
				for(int j=0;j<4;j++){
					cout<<g[i][j].x<<' '<<g[i][j].y;
				}
				cout<<endl;
			}*/
			disjointset ds = disjointset(m);
			for(int i=0;i<m;i++){
			    	polygon p = g[i];
				for(int j=0;j<m;j++){
				    	bool flag = false;
					for(int k=0;k<4;k++){
						if( contain(g[j],p[k])!=0 ){
							flag = true;
							break;
							//cout<<"ok"<<endl;
						}
					}
					for(int k =0 ;k<4;k++){
						for(int l = 0;l<4;l++){
							if(intersect(p[k],p[(k+1)%4],g[j][l],g[j][(l+1)%4] ) ){
								flag = true;
								break;
							}
						}
					}
					if(flag){
					    ds.unite(i,j);
					}
				}
			}
			int res = 0;
			for(int i=0;i<m;i++) if(ds.p[i] == i) res++;
			cout<<res<<endl;
		}
	}
	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3244
final_rank: 11

code_snippet: #include <iostream>
#include <vector>
#include <utility>
#include <complex>
using namespace std;

using D = double;
using P = complex<D>;
using L = pair<P,P>;
using Poly = vector<P>;

#define X real()
#define Y imag()

const D EPS = 1e-8;

D dot(const P a, const P b){ return a.X*b.X + a.Y*b.Y; }
D cross(const P a, const P b){ return a.X*b.Y - a.Y*b.X; }

int ccw(P a, P b, P c){
  b -= a, c -= a;
  if(cross(b,c) > EPS) return 1;
  if(cross(b,c) < -EPS) return -1;
  if(dot(b,c) < 0) return 2;
  if(norm(b) < norm(c) - EPS) return -2;
  return 0;
}

bool is_cross(L a, L b){
  P af = a.first, as = a.second, bf = b.first, bs = b.second;
  return (ccw(af,as,bf)*ccw(af,as,bs) <= 0 and ccw(bf,bs,af)*ccw(bf,bs,as) <= 0);
}

int in_poly(P a, const Poly &p){
  int n = p.size(), c = 0;
  for(int i = 0; i < n; ++i){
    P s  = p[i] - a, t = p[(i+1)%n] - a;
    if(not ccw(s,t,P(0,0))) return 1;
    if(s.Y > t.Y + EPS) swap(s,t);
    if((s.Y*t.Y < 0 or (s.Y*t.Y < EPS and t.Y > EPS)) and cross(s,t) < EPS) ++c;
  }
  if(c%2) return 2;
  return 0;
}

struct UnionFind{
private:
  vector<int> data;
public:
  int N;
  UnionFind(int n) : data(n,-1), N(n) {}
  bool unite(int x, int y){
    x = find(x);
    y = find(y);
    if(x == y) return false;
    --N;
    if(data[y] < data[x]) swap(x,y);
    data[x] += data[y];
    data[y] = x;
    return x != y;
  }
  int find(int x){
    if(data[x] < 0) return x;
    return (data[x] = find(data[x]));
  }
};

bool connected(const Poly &A, const Poly &B){
  int n = A.size(), m = B.size();
  for(auto p : A){
    if(in_poly(p,B)) return true;
  }
  for(auto p : B){
    if(in_poly(p,A)) return true;
  }
  for(size_t i = 0; i < A.size(); ++i){
    L a(A[i],A[(i+1)%n]);
    for(size_t j = 0; j < B.size(); ++j){
      L b(B[j],B[(j+1)%m]);
      if(is_cross(a,b)) return true;
    }
  }
  return false;
}


int main(){
  int N;
  while(cin >> N, N){
    for(int i = 0; i < N; ++i){
    vector<Poly> V;
      int M;
      cin >> M;
      UnionFind uf(M);
      for(int j = 0; j < M; ++j){
        Poly pol;
        for(int k = 0; k < 4; ++k){
          D x, y;
          cin >> x >> y;
          pol.emplace_back(x,y);
        }
        V.push_back(pol);
      }
      for(int j = 0; j < M; ++j){
        for(int k = 0; k < j; ++k){
          if(connected(V[j],V[k]))
            uf.unite(k,j);
        }
      }
      cout << uf.N << endl;
    }
  }
}
accuracy: 1
time_ms: 10
memory_kb: 3228
final_rank: 9

code_snippet: #include <bits/stdc++.h>
using namespace std;

using ld = long double;
using point = std::complex<ld>;
using polygon = std::vector<point>;

constexpr ld eps = 1e-10;
constexpr ld pi = std::acos(-1.0);

bool comp(point a, point b) {
    return std::real(a) < std::real(b) || (std::real(a) <= std::real(b) && std::imag(a) < std::imag(b));
}

std::vector<point> unique(std::vector<point> ps) {
    std::sort(std::begin(ps), std::end(ps), comp);
    std::vector<point> res;
    for(auto& p : ps) {
        if(res.empty() || abs(res.back() - p) > eps) {
            res.push_back(p);
        }
    }
    return res;
}

ld dot(point const& a, point const& b) {
    return std::real(std::conj(a) * b);
}
ld cross(point const& a, point const& b) {
    return std::imag(std::conj(a) * b);
}

int ccw(point a, point b, point c) {
    b -= a; c -= a;
    if(cross(b, c) > eps) return 1;            // a -> b -> c : counterclockwise
    if(cross(b, c) < -eps) return -1;          // a -> b -> c : clockwise
    if(dot(b, c) < 0) return 2;                // c -> a -> b : line
    if(std::norm(b) < std::norm(c)) return -2; // a -> b -> c : line
    return 0;                                  // a -> c -> b : line
}

struct segment {
    segment() : a(0, 0), b(0, 0) {}
    segment(point a, point b) : a(a), b(b) {}
    point a, b;
};

struct line {
    line() : a(0, 0), b(0, 0) {}
    line(point a, point b) : a(a), b(b) {}
    line(segment s) : a(s.a), b(s.b) {}
    point a, b;
};

struct circle {
    circle() : p(point(0, 0)), r(0) {}
    circle(point p_, long double r_) : p(p_), r(r_) {}
    point p;
    long double r;
};

bool isis_ll(line l, line m) {
    return cross(l.b - l.a, m.b - m.a) != 0;
}

bool isis_ls(line l, segment s) {
    return cross(l.b - l.a, s.a - l.a) * cross(l.b - l.a, s.b - l.a) <= 0;
}

bool isis_lp(line l, point p) {
    return (std::abs(cross(l.b - p, l.a - p)) < eps);
}

bool isis_sp(segment s, point p) {
    return (std::abs(s.a - p) + std::abs(s.b - p) - std::abs(s.b - s.a) < eps);
}

bool isis_ss(segment s, segment t) {
    return ccw(s.a, s.b, t.a) * ccw(s.a, s.b, t.b) <= 0
        && ccw(t.a, t.b, s.a) * ccw(t.a, t.b, s.b) <= 0;
}

point is_ll(line s, line t) {
    point sv = s.b - s.a, tv = t.b - t.a;
    assert(cross(sv, tv) != 0);
    return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}

point is_ss(segment s1, segment s2) {
    assert(isis_ss(s1, s2));
    point sv = s1.b - s1.a, tv = s2.b - s2.a;
    if(cross(sv, tv) == 0) {
        if(std::abs(s1.a - s2.a) < eps || std::abs(s1.a - s2.b) < eps) {
            return s1.a;
        } else {
            return s1.b;
        }
    } else {
        return is_ll(line(s1), line(s2));
    }
}

point proj(line l, point p) {
    long double t = dot(p - l.a, l.a - l.b) / std::norm(l.a - l.b);
    return l.a + t * (l.a - l.b);
}

class union_find {
public:
    union_find(int n) : par(n, -1) {}

    int root(int x) {
        return par[x] < 0 ? x : par[x] = root(par[x]);
    }

    bool unite(int x, int y) {
        x = root(x); y = root(y);
        if(x == y) return false;
        if(par[x] < par[y]) swap(x, y);
        par[x] += par[y];
        par[y] = x;
        return true;
    }

private:
    vector<int> par;
};

// 0 -> on, 1 -> in, 2 -> out
int is_in_polygon(polygon const& poly, point p) {
    const int n = poly.size();
    ld sum = 0;
    for(int i = 0; i < n; ++i) {
        point p1 = poly[i], p2 = poly[(i + 1) % n];
        if(isis_sp(segment(p1, p2), p)) {
            return 0;
        }
        sum += arg((p2 - p) / (p1 - p));
    }
    return std::abs(sum) < pi / 2 ? 2 : 1; // @todo: should check < 0.5 ?
}

bool isis_pp(polygon const& p1, polygon const& p2) {
    const int n = p1.size(), m = p2.size();
    for(int i = 0; i < n; ++i) {
        if(is_in_polygon(p2, p1[i]) != 2) return true;
    }
    for(int i = 0; i < m; ++i) {
        if(is_in_polygon(p1, p2[i]) != 2) return true;
    }
    for(int i = 0; i < n; ++i) {
        segment s1(p1[i], p1[(i + 1) % n]);
        for(int j = 0; j < m; ++j) {
            segment s2(p2[j], p2[(j + 1) % m]);
            if(isis_ss(s1, s2)) return true;
        }
    }
    return false;
}

int main() {
    int n;
    while(cin >> n, n) {
        for(int _ = 0; _ < n; ++_) {
            int m; cin >> m;
            int ans = m;
            vector<polygon> ps(m);
            union_find uf(m);
            for(int i = 0; i < m; ++i) {
                for(int j = 0; j < 4; ++j) {
                    int x, y; cin >> x >> y;
                    ps[i].emplace_back(x, y);
                }
            }
            for(int i = 0; i < m; ++i) {
                for(int j = i + 1; j < m; ++j) {
                    if(isis_pp(ps[i], ps[j])) {
                        ans -= uf.unite(i, j);
                    }
                }
            }
            cout << ans << endl;
        }
    }
}
accuracy: 1
time_ms: 140
memory_kb: 3132
final_rank: 20

code_snippet: #include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
#include<functional>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cassert>
#include<ctime>
#include<utility>
using namespace std;

#define mind(a,b) (a>b?b:a)
#define maxd(a,b) (a>b?a:b)
#define absd(x) (x<0?-(x):x)
#define pow2(x) ((x)*(x))
#define rep(i,n) for(int i=0; i<n; ++i)
#define repr(i,n) for(int i=n-1; i>=0; --i)
#define repl(i,s,n) for(int i=s; i<=n; ++i)
#define replr(i,s,n) for(int i=n; i>=s; --i)
#define repf(i,s,n,j) for(int i=s; i<=n; i+=j)
#define repe(e,obj) for(auto e : obj)

#define SP << " " <<
#define COL << " : " <<
#define COM << ", " <<
#define ARR << " -> " <<
#define PNT(STR) cout << STR << endl
#define POS(X,Y) "(" << X << ", " << Y << ")"
#define DEB(A) " (" << #A << ") " << A
#define DEBREP(i,n,val) for(int i=0; i<n; ++i) cout << val << " "; cout << endl
#define ALL(V) (V).begin(), (V).end()
#define INF 1000000007
#define INFLL 1000000000000000007LL
#define EPS 1e-9

typedef unsigned int uint;
typedef unsigned long ulong;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
#define P_TYPE int
typedef pair<P_TYPE, P_TYPE> P;
typedef pair<P, P_TYPE> PI;
typedef pair<P_TYPE, P> IP;
typedef pair<P, P> PP;
typedef priority_queue<P, vector<P>, greater<P> > pvqueue;

typedef int PType;
struct Point {
  PType x, y;
};

PType dot3(Point o, Point a, Point b) {
  return (a.x - o.x) * (b.x - o.x) + (a.y - o.y) * (b.y - o.y);
}
PType cross3(Point o, Point a, Point b) {
  return (a.x - o.x) * (b.y - o.y) - (b.x - o.x) * (a.y - o.y);
}
PType dist2(Point a, Point b) {
  return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
}

bool intersect(Point p0, Point p1, Point q0, Point q1) {
  PType c0 = cross3(p0, p1, q0), c1 = cross3(p0, p1, q1);
  PType d0 = cross3(q0, q1, p0), d1 = cross3(q0, q1, p1);
  if(c0 == 0 && c1 == 0) {
    PType e0 = dot3(p0, p1, q0);
    PType e1 = dot3(p0, p1, q1);
    if( !(e0 < e1) ) {
      swap(e0, e1);
    }
    return e0 <= dist2(p0, p1) && 0 <= e1;
  }
  return c0 * c1 <= 0 && d0 * d1 <= 0;
}

bool contains(vector<Point> ps, vector<Point> qs) {
  PType p[4];
  for(int i = 0; i < 4; ++i) {
    bool pos = true, neg = true;
    for(int j = 0; j < 4; ++j) {
      PType r = cross3(ps[j], ps[(j+1)%ps.size()], qs[i]);
      if(r < 0) pos = false;
      if(r > 0) neg = false;
    }
    if(pos || neg) {
      return true;
    }
  }
  return false;
}

bool check(vector<Point> ps, vector<Point> qs) {
  if(contains(ps, qs) || contains(qs, ps)) {
    return true;
  }
  for(int i = 0; i < 4; ++i) {
    for(int j = 0; j < 4; ++j) {
      if(intersect(ps[i], ps[(i+1)%ps.size()], qs[j], qs[(j+1)%qs.size()])) {
        return true;
      }
    }
  }
  return false;
}

#define M 103
int parent[M];
int root(int x) {
  if(x == parent[x]) return x;
  return parent[x] = root(parent[x]);
}
void unite(int x, int y) {
  int px = root(x), py = root(y);
  if(px < py) {
    parent[py] = px;
  } else {
    parent[px] = py;
  }
}

int main() {
  int n;
  while(cin >> n && n) {
    rep(i, n) {
      int m; cin >> m;
      rep(j, m) parent[j] = j;
      vector<vector<Point>> ps;
      rep(j, m) {
        vector<Point> p;
        rep(k, 4) {
          int x, y; cin >> x >> y;
          p.push_back(Point{x, y});
        }
        ps.push_back(p);
      }
      rep(i, m) {
        repl(j, i+1, m-1) {
          if(check(ps[i], ps[j])) {
            unite(i, j);
          }
        }
      }
      int res = 0;
      rep(i, m) {
        if(i == root(i)) ++res;
      }
      cout << res << endl;
    }
  }
  return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3088
final_rank: 4

code_snippet: #include <iostream>
#include <iomanip>
#include <complex>
#include <vector>
#include <algorithm>
#include <cmath>
#include <array>
using namespace std;

const double EPS = 1e-10;
const double INF = 1e12;
#define EQ(n,m) (abs((n)-(m)) < EPS)
#define X real()
#define Y imag()

typedef complex<double> P;
typedef vector<P> VP;
struct L : array<P, 2>{
    L(const P& a, const P& b){ at(0)=a; at(1)=b; }
    L(){}
};

double dot(P a, P b){
    return (conj(a)*b).X;
}
double cross(P a, P b){
    return (conj(a)*b).Y;
}
int ccw(P a, P b, P c){
    b -= a;
    c -= a;
    if(cross(b,c) > EPS) return +1; //ccw
    if(cross(b,c) < -EPS) return -1; //cw
    if(dot(b,c) < -EPS) return +2; //c-a-b
    if(abs(c)-abs(b) > EPS) return -2; //a-b-c
    return 0; //a-c-b
}

bool intersectSS(const L& a, const L& b){
    return ( ccw(a[0],a[1],b[0]) *ccw(a[0],a[1],b[1]) <= 0 ) &&
        ( ccw(b[0],b[1],a[0]) *ccw(b[0],b[1],a[1]) <= 0 );
}
bool intersectSP(const L& s, const P &p){
    return abs(cross(s[0]-p, s[1]-p))<EPS  && dot(s[0]-p, s[1]-p)<EPS;
}

int in_poly(const P &p, const VP &poly){
    int n = poly.size();
    int ret = -1;
    for(int i=0; i<n; i++){
        P a = poly[i]-p;
        P b = poly[(i+1)%n]-p;
        if(a.Y > b.Y) swap(a,b);
        if(intersectSP(L(a,b), P(0,0))) return 0;
        if(a.Y<=0 && b.Y>0 && cross(a,b)<0) ret = -ret;
    }
    return ret;
}

void dfs(int v, vector<bool> &used, vector<vector<int> > &adj){
    if(used[v]) return;
    used[v] = true;
    for(int nv: adj[v]){
        dfs(nv, used, adj);
    }
}

int main(){
    while(1){
        int n;
        cin >> n;
        if(n==0) break;
        
        for(int rep=0; rep<n; rep++){
            int m;
            cin >> m;
            vector<VP> sq(m, VP(4));
            for(int i=0; i<m; i++){
                for(int j=0; j<4; j++){
                    int x,y;
                    cin >> x >> y;
                    sq[i][j] = P(x, y);
                }
                swap(sq[i][0], sq[i][3]);
                swap(sq[i][1], sq[i][2]);
            }
            
            vector<vector<int> > adj(m);
            for(int i=0; i<m; i++){
                for(int j=i+1; j<m; j++){
                    bool overlap = false;
                    for(int k=0; k<4; k++){
                        if(in_poly(sq[i][k], sq[j]) >= 0 || in_poly(sq[j][k], sq[i]) >= 0){
                            overlap = true;
                        }
                        for(int l=0; l<4; l++){
                            if(intersectSS(L(sq[i][k], sq[i][(k+1)%4]), L(sq[j][l], sq[j][(l+1)%4]))){
                                overlap = true;
                            }
                        }
                    }
                    if(overlap){
                        adj[i].push_back(j);
                        adj[j].push_back(i);
                    }
                }
            }
            
            vector<bool> used(m, false);
            int count = 0;
            for(int i=0; i<m; i++){
                if(used[i]) continue;
                dfs(i, used, adj);
                count++;
            }
            cout << count << endl;
        }
    }
    return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3152
final_rank: 6

code_snippet: #include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

class Point{
public:
	double x,y;

	Point(double x = 0,double y = 0):x(x),y(y){}

	Point operator + (Point p){ return Point(x+p.x,y+p.y); }
	Point operator - (Point p){ return Point(x-p.x,y-p.y);}
	Point operator * (double a){ return Point(a*x,a*y); }
	Point operator / (double a){ return Point(x/a,y/a); }

	double abs(){ return sqrt(norm()); }
	double norm(){ return x*x + y*y; }

	bool operator<(const Point &p) const{
		return x != p.x? x < p.x: y < p.y;
	}

	bool operator == (const Point &p) const{
		return fabs(x-p.x) < EPS && fabs(y-p.y) < EPS;
	}
};

class Line{
public:
	Point p[2];
	Line(Point p1,Point p2){
		p[0] = p1;
		p[1] = p2;
	}
};

typedef Point Vector;
typedef vector<Point> Polygon;

int num_sets,num_point,Q;
int N;
int boss[100],height[100];
Polygon info[100];

int get_boss(int id){
	if(boss[id] == id)return id;
	else{
		return boss[id] = get_boss(boss[id]);
	}
}

int is_same(int x,int y){
	return get_boss(x) == get_boss(y);
}

void unite(int x,int y){
	int boss_x = get_boss(x);
	int boss_y = get_boss(y);

	if(boss_x == boss_y)return;

	if(height[x] > height[y]){

		boss[boss_y] = boss_x;

	}else if(height[x] < height[y]){

		boss[boss_x] = boss_y;

	}else{ //height[x] == height[y]

		boss[boss_y] = boss_x;
		height[x]++;
	}
}


double norm(Vector a){
	return a.x*a.x+a.y*a.y;
}

double abs(Vector a){
	return sqrt(norm(a));
}

double cross(Vector a,Vector b){
	return a.x*b.y-a.y*b.x;
}

double dot(Vector a,Vector b){
	return a.x*b.x + a.y*b.y;
}

Point calc_minus(Point a,Point b){
	Point ret;

	ret.x = a.x-b.x;
	ret.y = a.y-b.y;

	return ret;
}

int func(double x1,double y1,double x2, double y2, double xp, double yp){
	double naiseki,norm1,norm2,gaiseki;
	norm1 = sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
	norm2 = sqrt((xp-x1)*(xp-x1)+(yp-y1)*(yp-y1));
	naiseki = (xp-x1)*(x2-x1)+(yp-y1)*(y2-y1);
	gaiseki = (x2-x1)*(yp-y1)-(xp-x1)*(y2-y1);
	if(gaiseki > EPS){
		return 1;
	}else if(gaiseki < -EPS){
		return -1;
	}
	if(naiseki < -EPS){
		return 2;
	}

	if(norm1 < norm2){
		return -2;
	}
	return 0;
}

bool is_Cross(Line a,Line b){

	if(func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[0].x,b.p[0].y)*
			func(a.p[0].x,a.p[0].y,a.p[1].x,a.p[1].y,b.p[1].x,b.p[1].y) <= 0 &&
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[0].x,a.p[0].y)*
			func(b.p[0].x,b.p[0].y,b.p[1].x,b.p[1].y,a.p[1].x,a.p[1].y) <= 0){
		return true;
	}
	return false;
}

/*
 * IN 2
 * ON 1
 * OUT 0
 *
 */
int contains(Polygon g,Point p){
	int n = g.size();
	bool x = false;
	for(int i = 0; i < n; i++){
		Point a = g[i]-p,b = g[(i+1)%n]-p;
		if(abs(cross(a,b)) < EPS && dot(a,b) < EPS)return 1;
		if(a.y > b.y)swap(a,b);
		if(a.y < EPS && EPS < b.y && cross(a,b) > EPS) x = !x;
	}
	return (x ? 2:0);
}

bool intersect(const Polygon g1,const Polygon g2){

	int size_1 = g1.size(),size_2 = g2.size();

	for(int i = 0; i < size_1; i++){
		for(int k = 0; k < size_2; k++){
			if(is_Cross(Line(g1[i],g1[(i+1)%size_1]),Line(g2[k],g2[(k+1)%size_2]))){
				return true;
			}
		}
	}

	for(int i = 0; i < size_1; i++){
		if(contains(g2,g1[i]))return true;
	}

	for(int i = 0; i < size_2; i++){
		if(contains(g1,g2[i]))return true;
	}

	return false;
}

void func(){

	scanf("%d",&N);
	for(int i = 0; i < N; i++){
		boss[i] = i;
		height[i] = 0;
	}

	for(int i = 0; i < N; i++)info[i].clear();

	double x,y;
	for(int i = 0; i < N; i++){
		for(int k = 0; k < 4; k++){
			scanf("%lf %lf",&x,&y);
			info[i].push_back(Point(x,y));
		}
	}

	for(int i = 0; i < N; i++){
		for(int k = 0; k < N; k++){
			if(i == k)continue;

			if(intersect(info[i],info[k])){
				unite(i,k);
			}
		}
	}

	int ans = 0;
	for(int i = 0; i < N; i++){
		if(get_boss(i) == i)ans++;
	}

	printf("%d\n",ans);
}

int main(){

	while(true){
		scanf("%d",&num_sets);
		if(num_sets == 0)break;

		for(int i = 0; i < num_sets; i++)func();
	}

	return 0;
}
accuracy: 1
time_ms: 40
memory_kb: 3212
final_rank: 16

code_snippet: #include<bits/stdc++.h>
using namespace std;
#define rep(i, n) for ( int i = 0; i < (int)n; i++ )
#define EPS (1e-8)
class Point {
public:
	double x, y;

	Point(double x = 0, double y = 0) :
			x(x), y(y) {
	}
	Point operator +(Point p) {
		return Point(x + p.x, y + p.y);
	}
	Point operator -(Point p) {
		return Point(x - p.x, y - p.y);
	}
	Point operator *(double a) {
		return Point(x * a, y * a);
	}
};
typedef Point Vector;
typedef vector<Point> Polygon;
double norm(Vector a) {
	return a.x * a.x + a.y * a.y;
}
double dot(Vector a, Vector b) {
	return a.x * b.x + a.y * b.y;
}
double cross(Vector a, Vector b) {
	return a.x * b.y - a.y * b.x;
}
static const int COUNTER_CLOCKWISE = 1;
static const int CLOCKWISE = -1;
static const int ONLINE_BACK = 2;
static const int ONLINE_FRONT = -2;
static const int ON_SEGMENT = 0;
int ccw(Point p0, Point p1, Point p2) {
	Vector a = p1 - p0;
	Vector b = p2 - p0;
	if (cross(a, b) > EPS)
		return COUNTER_CLOCKWISE;
	if (cross(a, b) < -EPS)
		return CLOCKWISE;
	if (dot(a, b) < -EPS)
		return ONLINE_BACK;
	if (norm(a) < norm(b))
		return ONLINE_FRONT;
	return ON_SEGMENT;
}
bool isIntersect(Point p1, Point p2, Point p3, Point p4) {
	return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0
			&& ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0);
}
bool isInside(Polygon pol, Point p) {
	rep(i, 4)
	{
		if (ccw(pol[i], pol[(i + 1) % 4], p) == COUNTER_CLOCKWISE)
			return false;
	}
	return true;
}
bool isIntersectSQ(Polygon p1, Polygon p2) {
	rep(i, 3)
		rep(j, 3)
		{
			if (isIntersect(p1[i], p1[i + 1], p2[j], p2[j + 1]))
				return true;
		}
	rep(i, 4)
		if (isInside(p1, p2[i]))
			return true;
	rep(i, 4)
		if (isInside(p2, p1[i]))
			return true;
	return false;
}
Polygon P[100];
int n, G[100][100];
bool V[100];
//??????????????????????????????????????°???????????????????????????
//?????????V????????¨?????§?????????????´???\???????????°?????????????????°??????used true?????????
//???????????£??????????????¨????????????V????????????????????????????????????????????????
//G????????????????????????????????????????????????from to???????????????????????????V????????????
//??????G???????????§??§????????£????????????????????????V??????????????????????????§????????£??????????????£?????????
//?????°??????true??????????????????????????±??????true?????????????????????????????????
//

void dfs(int u) {
	V[u] = true;
	rep(v, n)
		if (G[u][v] && !V[v])
			dfs(v);
}
int compute() {
	rep(i, n)
		rep(j, n )
			G[i][j] = 0;
	rep(i, n)
		rep(j, n )
			if (isIntersectSQ(P[i], P[j]))
				G[i][j] = 1;
	rep(i, n)
		V[i] = false;
	int ncomponent = 0;
	rep(i, n)
	{
		if (V[i])
			continue;
		ncomponent++;
		dfs(i);
	}
	return ncomponent;
}
int main() {
	int N;
	double x, y;
	while (cin >> N && N) {
		rep(i, N)
		{
			cin >> n;
			rep(j, n)
			{
				Polygon pol;
				rep(k, 4)
				{
					cin >> x >> y;
					pol.push_back(Point(x, y));
				}
				P[j] = pol;
			}
			cout << compute() << endl;
		}
	}
	return 0;
}
accuracy: 1
time_ms: 10
memory_kb: 3248
final_rank: 12

code_snippet: #include <bits/stdc++.h>
  
using namespace std;
  
#define rep(i,n) REP(i,0,n)
#define REP(i,s,e) for(int i=(s); i<(int)(e); i++)
#define pb push_back
#define all(r) (r).begin(),(r).end()
#define rall(r) (r).rbegin(),(r).rend()
#define fi first
#define se second
  
typedef long long ll;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
 
const double EPS = 1e-8;
const double INF = 1e12;
const double PI = acos(-1);
typedef complex<double> P;
typedef complex<double> point;
namespace std {
	bool operator < (const P& a, const P& b) {
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
	}
}
double cross(const P& a, const P& b) {
	return imag(conj(a)*b);
}
double dot(const P& a, const P& b) {
	return real(conj(a)*b);
}

struct L : public vector<P> {
	L(const P &a, const P &b) {
		push_back(a); push_back(b);
	}
};

typedef vector<P> G;
typedef vector<P> polygon;

struct C {
	P p; double r;
	C(const P &p, double r) : p(p), r(r) { }
};
//??????????????????
int ccw(P a, P b, P c) {
	b -= a; c -= a;
	if (cross(b, c) > 0)   return +1;       // counter clockwise
	if (cross(b, c) < 0)   return -1;       // clockwise
	if (dot(b, c) < 0)     return +2;       // c--a--b on line
	if (norm(b) < norm(c)) return -2;       // a--b--c on line
	return 0;
}
bool intersectSS(const L &s, const L &t) {
  return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 &&
         ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;
}
bool intersectSP(const L &s, const P &p) {
	return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < EPS; // triangle inequality
}
#define curr(P, i) P[i]
#define next(P, i) P[(i+1)%P.size()]


//polygon??????????¨??????????????????????????????????
enum { OUT, ON, IN };
int contains(const G& pol, const P& p){
	if(pol.size() < 2) return OUT;
	if(intersectSP({pol[0], pol[1]}, p)) return ON;
	bool f = cross(pol[1]-pol[0], p - pol[0]) > 0;
	for(int i = 1; i < pol.size(); i++) {
		auto& now = pol[i];
		auto& nxt = pol[(i+1) % pol.size()];
		if(intersectSP({nxt, now}, p)) return ON;
		if(f^(cross(nxt-now, p-now)>0)) return OUT;
	}
	return IN;
}
//????????´???????£???????????????????(??????????????£???????????????)
enum{NO_HIT=0, HIT, A_IN_B, B_IN_A};
int HitPol_Pol(const polygon& a, const polygon& b){
	bool f = true, hit = false;
	rep(i, a.size()){
		if(contains(b, a[i]) == OUT) f = false;
		else hit = true;
	}
	if(f) return A_IN_B;
	f = true;
	rep(i, b.size()){
		if(contains(a, b[i]) == OUT) f = false;
		else hit = true;
	}
	if(f) return B_IN_A;
	if(hit) return HIT;
	rep(i, a.size()) rep(j, b.size()){
		L la(curr(a, i), next(a, i)), lb(curr(b, j), next(b, j));
		if(intersectSS(la, lb)) return HIT;
	}
	return NO_HIT;
}

struct UFT{ //O(loga(n))
    int n;
    vi d, r;
    UFT(int n) : n(n), d(n, -1), r(n, 0){};
    int root(int i){
        if(d[i] < 0) return i;
        return d[i] = root(d[i]);
    }
    bool same(int x, int y){
        return root(x) == root(y);
    }
    bool unite(int x, int y){
        x = root(x);
        y = root(y);
        if(x == y) return false;
 
        if(r[x] < r[y]) swap(x, y);
        else if(r[x] == r[y]) r[x]++;
        d[x] += d[y];
        d[y] = x;
        return true;
    }
    int size(int i){
        return -d[root(i)];
    }
};

int main(){
	int N;
	while(cin >> N && N) {
 		rep(_, N) {
 			int m;
 			cin >> m;
 			UFT uf(m);
 			vector<polygon> ps(m);
 			rep(i, m) {
 				double x, y;
 				rep(j,4) {
 					cin >> x>> y;
 					ps[i].pb({x, y});
 				}
 			}
 			rep(i, m) rep(j, i) {
 				if(HitPol_Pol(ps[i], ps[j])) uf.unite(i, j);
 			}
 			int ans = 0;
 			rep(i, m) if(uf.root(i) == i) ans++;
 			cout<<ans<<endl;
		}
	}
}
accuracy: 1
time_ms: 80
memory_kb: 3156
final_rank: 19

code_snippet: #ifndef ___Geometry_Library
#define ___Geometry_Library

// ------ Includes ------ //
#include <cmath>
#include <vector>
#include <complex>
#include <utility>
#include <algorithm>

// ------ Defines ------ //
typedef long double GType;
typedef std::complex<GType> Point;
typedef std::pair<Point, Point> Segment;

// ------ Constants ------ //
const GType EPS = 1.0e-10L;

// ------ Functions Level 1 ------ //
GType dot(const Point& p1, const Point& p2) {
	return (std::conj(p1) * p2).real();
}
GType cross(const Point& p1, const Point& p2) {
	return (std::conj(p1) * p2).imag();
}
int ccw(const Point& p1, const Point& p2, const Point& p3) {
	Point v1 = p2 - p1, v2 = p3 - p1;
	if (cross(v1, v2) > EPS) return +1;
	if (cross(v1, v2) < -EPS) return -1;
	if (dot(v1, v2) < -EPS) return +2;
	if (std::norm(v1) < std::norm(v2)) return -2;
	return 0;
}

// ------ Functions Level 2 ------ //
bool intersect(const Segment& s1, const Point& p1, bool segflag) {
	if (!segflag) return std::abs(cross(s1.second - p1, s1.first - p1)) < EPS;
	return (std::abs(s1.first - p1) + std::abs(s1.second - p1) - std::abs(s1.second - s1.first)) < EPS;
}
bool intersect(const Segment& s1, const Segment& s2, bool segflag) {
	if (!segflag) return cross(s1.second - s1.first, s2.first - s1.first) * cross(s1.second - s1.first, s2.second - s1.first) < EPS;
	return (ccw(s1.first, s1.second, s2.first) * ccw(s1.first, s1.second, s2.second) <= 0 && ccw(s2.first, s2.second, s1.first) * ccw(s2.first, s2.second, s1.second) <= 0);
}

// ------ Functions Level 3 ------ //
int contain(std::vector<Point> v, Point p) {
	bool in = false;
	for (int i = 0; i < v.size(); i++) {
		Point a = v[i] - p, b = v[(i + 1 != v.size() ? i + 1 : 0)] - p;
		if (a.imag() > b.imag()) swap(a, b);
		if (a.imag() <= EPS && EPS < b.imag() && cross(a, b) < -EPS) in = !in;
		if (cross(a, b) == 0 && dot(a, b) <= 0) return 1;
	}
	return in ? 2 : 0;
}

#endif

#include <vector>
#include <numeric>
#include <algorithm>

class unionfind {
private:
	int size_;
	std::vector<int> parent;
	std::vector<int> rank;
public:
	unionfind() : size_(0), parent(std::vector<int>()), rank(std::vector<int>()) {};
	unionfind(int size__) : size_(size__), parent(std::vector<int>(size_)), rank(std::vector<int>(size_, 0)) {
		std::iota(parent.begin(), parent.end(), 0);
	}
	int size() { return size_; }
	int root(int x) {
		if (parent[x] == x) return x;
		return parent[x] = root(parent[x]);
	}
	bool same(int x, int y) {
		return root(x) == root(y);
	}
	void unite(int x, int y) {
		x = root(x); y = root(y);
		if (x == y) return;
		if (rank[x] < rank[y]) parent[x] = y;
		else if (rank[x] > rank[y]) parent[y] = x;
		else parent[y] = x, rank[x]++;
	}
};

#include <set>
#include <iostream>
using namespace std;
int Q, N, x, y; vector<Point> p[109];
int main() {
	while (cin >> Q, Q) {
		while (Q--) {
			cin >> N;
			for (int i = 0; i < N; i++) {
				p[i] = vector<Point>(4);
				for (int j = 0; j < 4; j++) {
					cin >> x >> y;
					p[i][j] = Point(x, y);
				}
			}
			unionfind uf(N);
			for (int i = 0; i < N; i++) {
				for (int j = 0; j < N; j++) {
					bool flag = false;
					for (int k = 0; k < 4; k++) {
						if (contain(p[i], p[j][k])) flag = true;
					}
					for (int k = 0; k < 4; k++) {
						for (int l = 0; l < 4; l++) {
							if (intersect(Segment(p[i][k], p[i][(k + 1) % 4]), Segment(p[j][l], p[j][(l + 1) % 4]), true)) flag = true;
						}
					}
					if (flag) uf.unite(i, j);
				}
			}
			set<int> ret;
			for (int i = 0; i < N; i++) ret.insert(uf.root(i));
			cout << ret.size() << endl;
		}
	}
	return 0;
}
accuracy: 1
time_ms: 80
memory_kb: 3112
final_rank: 18

code_snippet: #include<iostream>
#include<vector>
#include<queue>
#include<cmath>
using namespace std;
struct Point { long double px, py; };
Point Minus(const Point& a, const Point& b) { return Point{ a.px - b.px,a.py - b.py }; }
long double norm(const Point& a) { return a.px * a.px + a.py * a.py; }
long double abs(const Point& a) { return sqrtl(norm(a)); }
long double arg(const Point& a) { return atan2l(a.py, a.px); }
long double dot(const Point& a, const Point& b) { return a.px * b.px + a.py * b.py; }
long double crs(const Point& a, const Point& b) { return a.px * b.py - a.py * b.px; }
int ccw(const Point& p0, const Point& p1, const Point& p2) {
	Point a = Minus(p1, p0), b = Minus(p2, p0);
	if (crs(a, b) > 1e-10) return 1;
	if (crs(a, b) < -1e-10) return -1;
	if (dot(a, b) < -1e-10) return 2;
	if (norm(a) < norm(b)) return -2;
	return 0;
}
bool its(const Point& p1, const Point& p2, const Point& p3, const Point& p4) { return (ccw(p1, p2, p3) * ccw(p1, p2, p4) <= 0 && ccw(p3, p4, p1) * ccw(p3, p4, p2) <= 0); }
int contain(vector<Point> v, Point p) {
	bool in = false;
	for (int i = 0; i < v.size(); ++i) {
		Point a = Minus(v[i], p), b = Minus(v[(i + 1) % v.size()], p);
		if (a.py > b.py) swap(a, b);
		if (a.py <= 0 && 0 < b.py)
			if (crs(a, b) < 0) in = !in;
		if (crs(a, b) == 0 && dot(a, b) <= 0) return 1;
	}
	return in ? 2 : 0;
}
bool connect(vector<Point>p1, vector<Point>p2) {
	for (int i = 0; i < p1.size(); i++) {
		for (int j = 0; j < p2.size(); j++) {
			if (its(p1[i], p1[(i + 1) % p1.size()], p2[j], p2[(j + 1) % p2.size()]) == true)return true;
		}
	}
	for (int i = 0; i < p2.size(); i++) { if (contain(p1, p2[i]) != 0)return true; }
	for (int i = 0; i < p1.size(); i++) { if (contain(p2, p1[i]) != 0)return true; }
	return false;
}
vector<Point>r[200]; int n, t; vector<int>X[200]; bool unused[200];
int main() {
	while (true) {
		cin >> t; if (t == 0)break;
		for (int h = 0; h < t; h++) {
			for (int i = 0; i < 200; i++) { r[i].clear(); r[i].resize(4); X[i].clear(); unused[i] = false; }
			cin >> n; int cnt = 0;
			for (int j = 0; j < n; j++) {
				cin >> r[cnt][0].px >> r[cnt][0].py >> r[cnt][1].px >> r[cnt][1].py;
				cin >> r[cnt][2].px >> r[cnt][2].py >> r[cnt][3].px >> r[cnt][3].py; cnt++;
			}
			for (int i = 0; i < cnt; i++) {
				for (int j = i + 1; j < cnt; j++) {
					if (connect(r[i], r[j]) == true) { X[i].push_back(j); X[j].push_back(i); }
				}
			}
			queue<int>Q; int ans = 0;
			for (int i = 0; i < cnt; i++) {
				if (unused[i] == true)continue;
				Q.push(i); unused[i] = true; ans++;
				while (!Q.empty()) {
					int a1 = Q.front(); Q.pop();
					for (int j = 0; j < X[a1].size(); j++) {
						if (unused[X[a1][j]] == false) { unused[X[a1][j]] = true; Q.push(X[a1][j]); }
					}
				}
			}
			cout << ans << endl;
		}
	}
	return 0;
}
accuracy: 1
time_ms: 20
memory_kb: 3188
final_rank: 14

code_snippet: /*include*/
#include<iostream>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<vector>
#include<cmath>
#include<cstdio>
#include<complex>
#define loop(i,a,b) for(int i=a;i<b;i++) 
#define rep(i,a) loop(i,0,a)
#define rp(a) while(a--)
#define pb push_back
#define mp make_pair
#define it ::iterator
#define all(in) in.begin(),in.end()
const double PI=acos(-1);
const double EPS=1e-10;
const double inf=1e8;
using namespace std;
typedef complex<double> P;
typedef vector<P> G;
typedef vector<int> vi;
typedef vector<vi> vvi;

struct L : public vector<P> {
  L(const P &a, const P &b) {
    push_back(a); push_back(b);
  }
};
struct C{
	P c;double r;
	C(const P &c,double r):c(c),r(r){}
};
#define curr(P, i) P[i]
#define next(P, i) P[(i+1)%P.size()]
#define diff(P, i) (next(P, i) - curr(P, i))
namespace std {
	bool operator < (const P& a, const P& b) {
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
		//return imag(a) != imag(b) ? imag(a) < imag(b) : real(a) < real(b);  
	}
	bool operator == (const P& a, const P& b) {
		return a.real()==b.real()&&a.imag()==b.imag();
	}
}
P pin(){
	double x,y;
	char d;
	cin>>x>>y;
	P p(x,y);
	return p;
}
void PIN(P* a,int n){
	rep(i,n)a[i]=pin();
}
double dot(P a,P b){
	return real(conj(a)*b);
}
double cross(P a,P b){
	return imag(conj(a)*b);
}
int ccw(P a, P b, P c) {
  b -= a; c -= a;
  if (cross(b, c) > 0)   return +1;       // counter clockwise
  if (cross(b, c) < 0)   return -1;       // clockwise
  if (dot(b, c) < 0)     return +2;       // c--a--b on line
  if (norm(b) < norm(c)) return -2;       // a--b--c on line
  return 0;
}
P projection(L a,P p){
	double t=dot(p-a[0],a[0]-a[1])/norm(a[0]-a[1]);
	return a[0]+t*(a[0]-a[1]);
}
P reflection(L a,P p){
	return p+2.0*(projection(a,p)-p);
}
bool intersectLL(const L &l, const L &m) {
  return abs(cross(l[1]-l[0], m[1]-m[0])) > EPS || // non-parallel
         abs(cross(l[1]-l[0], m[0]-l[0])) < EPS;   // same line
}
bool intersectLS(const L &l, const L &s) {
  return cross(l[1]-l[0], s[0]-l[0])*       // s[0] is left of l
         cross(l[1]-l[0], s[1]-l[0]) < EPS; // s[1] is right of l
}
bool intersectLP(const L &l, const P &p) {
  return abs(cross(l[1]-p, l[0]-p)) < EPS;
}
bool intersectSS(const L &s, const L &t) {
  return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 &&
         ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;
}
bool intersectSP(const L &s, const P &p) {
  return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < EPS; // triangle inequality
}
double distanceLP(const L &l, const P &p) {
	return abs(p - projection(l, p));
}
double distanceLL(const L &l, const L &m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m[0]);
}
double distanceLS(const L &l, const L &s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
double distanceSP(const L &s, const P &p) {
  const P r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s[0] - p), abs(s[1] - p));
}
double distanceSS(const L &s, const L &t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t[0]), distanceSP(s, t[1])),
             min(distanceSP(t, s[0]), distanceSP(t, s[1])));
}
/*bool intersectCS(C c,const L &l){
    return (distanceLP(l,c.c) < c.r+EPS &&
            (c.r < abs(c.c-l[0]) + EPS || c.r < abs(c.c-l[1]) + EPS));
}*/
int intersectCS(C c,L &l){
	if(norm(projection(l,c.c)-c.c)-c.r*c.r>EPS)return 0;
	const double d1=abs(c.c-l[0]),d2=abs(c.c-l[1]);
	if(d1<c.r+EPS&&d2<c.r+EPS)return 0;
	if(d1<c.r-EPS&&d2>c.r+EPS||d1>c.r+EPS&&d2<c.r-EPS)return 1;
	const P h=projection(l,c.c);
	if(dot(l[0]-h,l[1]-h)<0)return 2;
	return 0;
}
P crosspointSS(L a,L b){
	double t1=abs(cross(a[1]-a[0],b[0]-a[0]));
	double t2=abs(cross(a[1]-a[0],b[1]-a[0]));
	return b[0]+(b[1]-b[0])*t1/(t1+t2);
}
L crosspointCL(C c,L l){
	P pr=projection(l,c.c);
	P e=(l[1]-l[0])/abs(l[1]-l[0]);
	double t=sqrt(c.r*c.r-norm(pr-c.c));
	P a=pr+t*e;
	P b=pr-t*e;
	if(b<a)swap(a,b);
	return L(a,b);
}
L crosspointCS(C c,L l){
	if(intersectCS(c,l)==2)return crosspointCL(c,l);
	L ret=crosspointCL(c,l);
	if(dot(l[0]-ret[0],l[1]-ret[0])<0)ret[1]=ret[0];
	else ret[0]=ret[1];
	return ret;
}
L crosspointCC(C a,C b){
	P tmp=b.c-a.c;
	double d=abs(tmp);
	double q=acos((a.r*a.r+d*d-b.r*b.r)/(2*a.r*d));
	double t=arg(tmp);//atan(tmp.imag()/tmp.real());
	P p1=a.c+polar(a.r,t+q);
	P p2=a.c+polar(a.r,t-q);
	if(p2<p1)swap(p1,p2);
	return L(p1,p2);
}
P crosspointLL(const L &l, const L &m) {
  double A = cross(l[1] - l[0], m[1] - m[0]);
  double B = cross(l[1] - l[0], l[1] - m[0]);
  if (abs(A) < EPS && abs(B) < EPS) return m[0]; // same line
  return m[0] + B / A * (m[1] - m[0]);
}
double area(const G &g){
    double S =0;
    for(int i =0;i <g.size();i++){
        S +=(cross(g[i],g[(i+1)%g.size()]));
    }
    return abs(S/2.0);
}
bool isconvex(const G &g){
	int n=g.size();
	rep(i,n)if(ccw(g[(i+n-1)%n],g[i%n],g[(i+1)%n])==-1)return false;
	return true;
}
int inconvex(const G& g, const P& p) {
	bool in = false;
	int n=g.size();
	rep(i,n){
		P a=g[i%n]-p;
		P b=g[(i+1)%n]-p;
		if(imag(a)>imag(b))swap(a, b);
		if(imag(a)<=0&&0<imag(b))if(cross(a,b)<0)in=!in;
		if(cross(a,b)==0&&dot(a,b)<=0)return 1;//ON
	}
	return in?2:0;//IN : OUT;
}
G convex_hull(G &ps) {
    int n=ps.size(),k=0;
	sort(ps.begin(), ps.end());
	G ch(2*n);
	for(int i=0;i<n;ch[k++]=ps[i++])//lower-hull
		while(k>=2&&ccw(ch[k-2],ch[k-1],ps[i])==-1)--k;//<=0  ->  ==-1
	for(int i=n-2,t=k+1;i>=0;ch[k++]=ps[i--])//upper-hull
		while(k>=t&&ccw(ch[k-2],ch[k-1],ps[i])==-1)--k;//
	ch.resize(k-1);
	return ch;
}
double convex_diameter(const G &pt) {
  const int n = pt.size();
  int is = 0, js = 0;
  for (int i = 1; i < n; ++i) {
    if (imag(pt[i]) > imag(pt[is])) is = i;
    if (imag(pt[i]) < imag(pt[js])) js = i;
  }
  double maxd = norm(pt[is]-pt[js]);

  int i, maxi, j, maxj;
  i = maxi = is;
  j = maxj = js;
  do {
    if (cross(diff(pt,i), diff(pt,j)) >= 0) j = (j+1) % n;
    else i = (i+1) % n;
    if (norm(pt[i]-pt[j]) > maxd) {
      maxd = norm(pt[i]-pt[j]);
      maxi = i; maxj = j;
    }
  } while (i != is || j != js);
  return sqrt(maxd); /* farthest pair is (maxi, maxj). */
}//convex_diameter(g)
G convex_cut(const G& g, const L& l) {
  G Q;
  for (int i = 0; i < g.size(); ++i) {
    P a= curr(g, i), b= next(g, i);
    if (ccw(l[0], l[1], a) != -1) Q.push_back(a);
    if (ccw(l[0], l[1], a)*ccw(l[0], l[1], b) < 0)
      Q.push_back(crosspointLL(L(a,b), l));
  }
  return Q;
}
P turn(P p,double t){
	return p*exp(P(.0,t*PI/180.0));
}
vector<L> tangentCC(C a,C b){
	if(a.r<b.r)swap(a,b);
	double d=abs(a.c-b.c);
	vector<L>l;
	if(d<EPS)return l;
	if(a.r+b.r<d-EPS){//hanareteiru
		double t=acos((a.r+b.r)/d);
		t=t*180/PI;
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),t),b.c+turn(b.r/d*(a.c-b.c),t)));
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),-t),b.c+turn(b.r/d*(a.c-b.c),-t)));
	}else if(a.r+b.r<d+EPS){//kuttuiteiru soto
		P p=a.c+a.r/d*(b.c-a.c);
		l.pb(L(p,p+turn(b.c-a.c,90)));
	}
	if(abs(a.r-b.r)<d-EPS){//majiwatteiru
		double t1=acos((a.r-b.r)/d);
		t1=t1*180/PI;
		double t2=180-t1;
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),t1),b.c+turn(b.r/d*(a.c-b.c),-t2)));
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),-t1),b.c+turn(b.r/d*(a.c-b.c),t2)));
	}else if(abs(a.r-b.r)<d+EPS){//kuttuiteiru uti
		P p=a.c+a.r/d*(b.c-a.c);
		l.pb(L(p,p+turn(b.c-a.c,90)));
	}
	return l;
}
void printL(const L &out){
	printf("%.9f %.9f %.9f %.9f\n",out[0].real(),out[0].imag(),out[1].real(),out[1].imag());
}
C CIN(){
	P p=pin();
	double r;
	cin>>r;
	return C(p,r);
}
bool para(L a,L b){
	return (abs(cross(a[1]-a[0],b[1]-b[0]))<EPS);
}
struct UnionFind{
    vector<int> data;
    UnionFind(int size) : data(size, -1) {}
    bool unionSet(int x,int y){
        x=root(x);y=root(y);
        if(x==y)return 0;
        if(data[y]<data[x])swap(x,y);
        data[x]+=data[y],data[y]=x;
        return 1;
    }
    bool findSet(int x,int y){
        return root(x)==root(y);
    }
    int root(int x){
        return data[x]<0 ? x : data[x]=root(data[x]);
    }
    int size(int x){
        return -data[root(x)];
    }
	int co(){
		int c=0;
		rep(i,data.size())if(data[i]<0)c++;
		return c;
	}
};
int main(){
	int n;
	while(cin>>n,n){
		while(n--){
			int m;cin>>m;
			vector<G>g(m,G(4));
			rep(i,m)rep(j,4)g[i][j]=pin();
			UnionFind uf(m);
			rep(i,m)loop(j,i+1,m){
				bool h=true;
				rep(k,4)rep(l,4)if(intersectSS(L(g[i][k],g[i][(k+1)%4]),L(g[j][l],g[j][(l+1)%4])))h=false;
				if(h){
					if(inconvex(g[i],g[j][0])||inconvex(g[j],g[i][0]))h=false;
				}
				if(!h)uf.unionSet(i,j);
			}
			cout<<uf.co()<<endl;
		}
	}
}
accuracy: 1
time_ms: 60
memory_kb: 1276
final_rank: 2

code_snippet: /*include*/
#include<iostream>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<vector>
#include<cmath>
#include<cstdio>
#include<complex>
#define loop(i,a,b) for(int i=a;i<b;i++) 
#define rep(i,a) loop(i,0,a)
#define rp(a) while(a--)
#define pb push_back
#define mp make_pair
#define it ::iterator
#define all(in) in.begin(),in.end()
const double PI=acos(-1);
const double EPS=1e-10;
const double inf=1e8;
using namespace std;
typedef complex<double> P;
typedef vector<P> G;
typedef vector<int> vi;
typedef vector<vi> vvi;

struct L : public vector<P> {
  L(const P &a, const P &b) {
    push_back(a); push_back(b);
  }
};
struct C{
	P c;double r;
	C(const P &c,double r):c(c),r(r){}
};
#define curr(P, i) P[i]
#define next(P, i) P[(i+1)%P.size()]
#define diff(P, i) (next(P, i) - curr(P, i))
namespace std {
	bool operator < (const P& a, const P& b) {
		return real(a) != real(b) ? real(a) < real(b) : imag(a) < imag(b);
		//return imag(a) != imag(b) ? imag(a) < imag(b) : real(a) < real(b);  
	}
	bool operator == (const P& a, const P& b) {
		return a.real()==b.real()&&a.imag()==b.imag();
	}
}
P pin(){
	double x,y;
	char d;
	cin>>x>>y;
	P p(x,y);
	return p;
}
void PIN(P* a,int n){
	rep(i,n)a[i]=pin();
}
double dot(P a,P b){
	return real(conj(a)*b);
}
double cross(P a,P b){
	return imag(conj(a)*b);
}
int ccw(P a, P b, P c) {
  b -= a; c -= a;
  if (cross(b, c) > 0)   return +1;       // counter clockwise
  if (cross(b, c) < 0)   return -1;       // clockwise
  if (dot(b, c) < 0)     return +2;       // c--a--b on line
  if (norm(b) < norm(c)) return -2;       // a--b--c on line
  return 0;
}
P projection(L a,P p){
	double t=dot(p-a[0],a[0]-a[1])/norm(a[0]-a[1]);
	return a[0]+t*(a[0]-a[1]);
}
P reflection(L a,P p){
	return p+2.0*(projection(a,p)-p);
}
bool intersectLL(const L &l, const L &m) {
  return abs(cross(l[1]-l[0], m[1]-m[0])) > EPS || // non-parallel
         abs(cross(l[1]-l[0], m[0]-l[0])) < EPS;   // same line
}
bool intersectLS(const L &l, const L &s) {
  return cross(l[1]-l[0], s[0]-l[0])*       // s[0] is left of l
         cross(l[1]-l[0], s[1]-l[0]) < EPS; // s[1] is right of l
}
bool intersectLP(const L &l, const P &p) {
  return abs(cross(l[1]-p, l[0]-p)) < EPS;
}
bool intersectSS(const L &s, const L &t) {
  return ccw(s[0],s[1],t[0])*ccw(s[0],s[1],t[1]) <= 0 &&
         ccw(t[0],t[1],s[0])*ccw(t[0],t[1],s[1]) <= 0;
}
bool intersectSP(const L &s, const P &p) {
  return abs(s[0]-p)+abs(s[1]-p)-abs(s[1]-s[0]) < EPS; // triangle inequality
}
double distanceLP(const L &l, const P &p) {
	return abs(p - projection(l, p));
}
double distanceLL(const L &l, const L &m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m[0]);
}
double distanceLS(const L &l, const L &s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s[0]), distanceLP(l, s[1]));
}
double distanceSP(const L &s, const P &p) {
  const P r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s[0] - p), abs(s[1] - p));
}
double distanceSS(const L &s, const L &t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t[0]), distanceSP(s, t[1])),
             min(distanceSP(t, s[0]), distanceSP(t, s[1])));
}
/*bool intersectCS(C c,const L &l){
    return (distanceLP(l,c.c) < c.r+EPS &&
            (c.r < abs(c.c-l[0]) + EPS || c.r < abs(c.c-l[1]) + EPS));
}*/
int intersectCS(C c,L &l){
	if(norm(projection(l,c.c)-c.c)-c.r*c.r>EPS)return 0;
	const double d1=abs(c.c-l[0]),d2=abs(c.c-l[1]);
	if(d1<c.r+EPS&&d2<c.r+EPS)return 0;
	if(d1<c.r-EPS&&d2>c.r+EPS||d1>c.r+EPS&&d2<c.r-EPS)return 1;
	const P h=projection(l,c.c);
	if(dot(l[0]-h,l[1]-h)<0)return 2;
	return 0;
}
P crosspointSS(L a,L b){
	double t1=abs(cross(a[1]-a[0],b[0]-a[0]));
	double t2=abs(cross(a[1]-a[0],b[1]-a[0]));
	return b[0]+(b[1]-b[0])*t1/(t1+t2);
}
L crosspointCL(C c,L l){
	P pr=projection(l,c.c);
	P e=(l[1]-l[0])/abs(l[1]-l[0]);
	double t=sqrt(c.r*c.r-norm(pr-c.c));
	P a=pr+t*e;
	P b=pr-t*e;
	if(b<a)swap(a,b);
	return L(a,b);
}
L crosspointCS(C c,L l){
	if(intersectCS(c,l)==2)return crosspointCL(c,l);
	L ret=crosspointCL(c,l);
	if(dot(l[0]-ret[0],l[1]-ret[0])<0)ret[1]=ret[0];
	else ret[0]=ret[1];
	return ret;
}
L crosspointCC(C a,C b){
	P tmp=b.c-a.c;
	double d=abs(tmp);
	double q=acos((a.r*a.r+d*d-b.r*b.r)/(2*a.r*d));
	double t=arg(tmp);//atan(tmp.imag()/tmp.real());
	P p1=a.c+polar(a.r,t+q);
	P p2=a.c+polar(a.r,t-q);
	if(p2<p1)swap(p1,p2);
	return L(p1,p2);
}
P crosspointLL(const L &l, const L &m) {
  double A = cross(l[1] - l[0], m[1] - m[0]);
  double B = cross(l[1] - l[0], l[1] - m[0]);
  if (abs(A) < EPS && abs(B) < EPS) return m[0]; // same line
  return m[0] + B / A * (m[1] - m[0]);
}
double area(const G &g){
    double S =0;
    for(int i =0;i <g.size();i++){
        S +=(cross(g[i],g[(i+1)%g.size()]));
    }
    return abs(S/2.0);
}
bool isconvex(const G &g){
	int n=g.size();
	rep(i,n)if(ccw(g[(i+n-1)%n],g[i%n],g[(i+1)%n])==-1)return false;
	return true;
}
int inconvex(const G& g, const P& p) {
	bool in = false;
	int n=g.size();
	rep(i,n){
		P a=g[i%n]-p;
		P b=g[(i+1)%n]-p;
		if(imag(a)>imag(b))swap(a, b);
		if(imag(a)<=0&&0<imag(b))if(cross(a,b)<0)in=!in;
		if(cross(a,b)==0&&dot(a,b)<=0)return 1;//ON
	}
	return in?2:0;//IN : OUT;
}
G convex_hull(G &ps) {
    int n=ps.size(),k=0;
	sort(ps.begin(), ps.end());
	G ch(2*n);
	for(int i=0;i<n;ch[k++]=ps[i++])//lower-hull
		while(k>=2&&ccw(ch[k-2],ch[k-1],ps[i])==-1)--k;//<=0  ->  ==-1
	for(int i=n-2,t=k+1;i>=0;ch[k++]=ps[i--])//upper-hull
		while(k>=t&&ccw(ch[k-2],ch[k-1],ps[i])==-1)--k;//
	ch.resize(k-1);
	return ch;
}
double convex_diameter(const G &pt) {
  const int n = pt.size();
  int is = 0, js = 0;
  for (int i = 1; i < n; ++i) {
    if (imag(pt[i]) > imag(pt[is])) is = i;
    if (imag(pt[i]) < imag(pt[js])) js = i;
  }
  double maxd = norm(pt[is]-pt[js]);

  int i, maxi, j, maxj;
  i = maxi = is;
  j = maxj = js;
  do {
    if (cross(diff(pt,i), diff(pt,j)) >= 0) j = (j+1) % n;
    else i = (i+1) % n;
    if (norm(pt[i]-pt[j]) > maxd) {
      maxd = norm(pt[i]-pt[j]);
      maxi = i; maxj = j;
    }
  } while (i != is || j != js);
  return sqrt(maxd); /* farthest pair is (maxi, maxj). */
}//convex_diameter(g)
G convex_cut(const G& g, const L& l) {
  G Q;
  for (int i = 0; i < g.size(); ++i) {
    P a= curr(g, i), b= next(g, i);
    if (ccw(l[0], l[1], a) != -1) Q.push_back(a);
    if (ccw(l[0], l[1], a)*ccw(l[0], l[1], b) < 0)
      Q.push_back(crosspointLL(L(a,b), l));
  }
  return Q;
}
P turn(P p,double t){
	return p*exp(P(.0,t*PI/180.0));
}
vector<L> tangentCC(C a,C b){
	if(a.r<b.r)swap(a,b);
	double d=abs(a.c-b.c);
	vector<L>l;
	if(d<EPS)return l;
	if(a.r+b.r<d-EPS){//hanareteiru
		double t=acos((a.r+b.r)/d);
		t=t*180/PI;
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),t),b.c+turn(b.r/d*(a.c-b.c),t)));
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),-t),b.c+turn(b.r/d*(a.c-b.c),-t)));
	}else if(a.r+b.r<d+EPS){//kuttuiteiru soto
		P p=a.c+a.r/d*(b.c-a.c);
		l.pb(L(p,p+turn(b.c-a.c,90)));
	}
	if(abs(a.r-b.r)<d-EPS){//majiwatteiru
		double t1=acos((a.r-b.r)/d);
		t1=t1*180/PI;
		double t2=180-t1;
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),t1),b.c+turn(b.r/d*(a.c-b.c),-t2)));
		l.pb(L(a.c+turn(a.r/d*(b.c-a.c),-t1),b.c+turn(b.r/d*(a.c-b.c),t2)));
	}else if(abs(a.r-b.r)<d+EPS){//kuttuiteiru uti
		P p=a.c+a.r/d*(b.c-a.c);
		l.pb(L(p,p+turn(b.c-a.c,90)));
	}
	return l;
}
void printL(const L &out){
	printf("%.9f %.9f %.9f %.9f\n",out[0].real(),out[0].imag(),out[1].real(),out[1].imag());
}
C CIN(){
	P p=pin();
	double r;
	cin>>r;
	return C(p,r);
}
bool para(L a,L b){
	return (abs(cross(a[1]-a[0],b[1]-b[0]))<EPS);
}
struct UnionFind{
    vector<int> data;
    UnionFind(int size) : data(size, -1) {}
    bool unionSet(int x,int y){
        x=root(x);y=root(y);
        if(x==y)return 0;
        if(data[y]<data[x])swap(x,y);
        data[x]+=data[y],data[y]=x;
        return 1;
    }
    bool findSet(int x,int y){
        return root(x)==root(y);
    }
    int root(int x){
        return data[x]<0 ? x : data[x]=root(data[x]);
    }
    int size(int x){
        return -data[root(x)];
    }
	int co(){
		int c=0;
		rep(i,data.size())if(data[i]<0)c++;
		return c;
	}
};
int main(){
	int n;
	while(cin>>n,n){
		while(n--){
			vector<G>g;
			int m;cin>>m;
			rep(j,m){
				G t(4);
				rep(k,4)t[k]=pin();
				g.pb(t);
			}
			UnionFind uf(m);
			rep(i,m)loop(j,i+1,m){
				bool h=true;
				rep(k,4)if(inconvex(g[i],g[j][k])||inconvex(g[j],g[i][k]))h=false;
				rep(k,4)rep(l,4)if(intersectSS(L(g[i][k],g[i][(k+1)%4]),L(g[j][l],g[j][(l+1)%4])))h=false;
				if(!h)uf.unionSet(i,j);
			}		
			cout<<uf.co()<<endl;
		}
	}
}
accuracy: 1
time_ms: 70
memory_kb: 1280
final_rank: 3

code_snippet: #define _USE_MATH_DEFINES
#include<bits/stdc++.h>
using namespace std;

const int COUNTER_CLOCKWISE = +1;
const int CLOCKWISE = -1;
const int ONLINE_BACK = +2;
const int ONLINE_FRONT = -2;
const int ON_SEGMENT = 0;

const double EPS=1e-6;
const double INF=1e10;
const double PI=acos(-1);

typedef complex<double> Point;
typedef complex<double> Vector;
typedef vector<Point> Polygon;

struct Line
{
	Point a, b;
	Line() {}
	Line(const Point &_a, const Point &_b)
	{
		a = _a; b = _b;
	}
};

//Line????????????
struct Segment
{
	Point a, b;
	Segment() {}
	Segment(const Point &_a, const Point &_b)
	{
		a = _a; b = _b;
	}
};

struct Circle
{
	Point c;
	double r;

	Circle() {}

	Circle(const Point &_c, const double &_r)
	{
		c = _c; r = _r;
	}
};

istream& operator >> (istream& is, Point &p)
{
	double x, y;
	is >> x >> y;
	p.real(x); p.imag(y);
	return is;
}

Segment ToSegment(const Line l)
{
	return Segment(l.a, l.b);
}

Line ToLine(const Segment s)
{
	return Line(s.a, s.b);
}

//???????¨±???==
bool equals(const double &a, const double &b);
bool equals(const Vector &a, const Vector &b);

//Point?????????
bool comp(const Point &a, const Point &b);

//?§????????????????
double toRad(double a);
double toDeg(double a);

//?????¢??????
Point rotate(Point p,double d);


//??????
double dot(const Vector &a, const Vector &b);

//??????
double cross(const Vector &a, const Vector &b);

//????§???¢?????¢???
double GetArea(const Polygon &g);

//???????¨???????(O(1))
//?????¶:????????¨?????°??????
int CCW(const Point &p0, const Point &p1, const Point &p2);

//??´?????????(O(1))
bool IsOrthogonal(const Vector &a, const Vector &b);
bool IsOrthogonal(const Line &l1, const Line &l2);

//????????????(O(1))
bool IsParallel(const Vector &a, const Vector &b);
bool IsParallel(const Line &l1, const Line &l2);

//?°???±(O(1))
Point Project(const Line &s, const Point &p);

//????°?(O(1))
Point Reflect(const Line &s, const Point &p);

//?????¨??´???????????¢
double GetDistance(const Line &l, const Point &p);

//?????¨??????????????¢
double GetDistance(const Segment &s, const Point &p);

//????????¨??????????????¢
double GetDistance(const Segment &s1, const Segment &s2);

//??´?????¨?????????????????????(O(1))
bool Intersects(const Line &l, const Segment &s);

//??????????????????
bool Intersects(const Segment &l1, const Segment &l2);

//??´?????¨??´????????????(O(1))
Point GetIntersection(const Line &l1, const Line &l2);

//?????¨????????????
//?????????
//	???????????????????????°
vector<Point> GetIntersection(const Circle &c1, const Circle &c2);

//????§???¢????????????(O(n))
//?????????
//  0:???????????????
//  1:????????????
//  2:?????????
int Contains(const Polygon &g, const Point &p);

//??????(O(n))
//?????????
//	x->y????????§???????°??????????????????????????¨???????
Polygon AndrewScan(Polygon &s);

//???????§???¢?????????(O(n))
Polygon ConvexCut(const Polygon &g, const Line &l);


//-----------------------------------------------------------

bool equals(const double &a, const double &b)
{
	return abs(a - b) < EPS;
}

bool equals(const Vector &a, const Vector &b)
{
	return equals(a.real(), b.real()) && equals(a.imag(), b.imag());
}

bool comp(const Point &a, const Point &b)
{
	return equals(a.real(), b.real()) ? a.imag() < b.imag() : a.real() < b.real();
}

double dot(const Vector &a, const Vector &b)
{
	return a.real()*b.real() + a.imag()*b.imag();
}

double cross(const Vector &a, const Vector &b)
{
	return a.real()*b.imag() - a.imag()*b.real();
}

double GetArea(const Polygon &g)
{
    if(g.size()<3)return 0;
	double res = 0;
	for (int i = 1; i < g.size() - 1; i++)
	{
		res += cross(g[i] - g[0], g[i + 1] - g[0]);
	}
	return abs(res / 2);
}

int CCW(const Point &p0, const Point &p1, const Point &p2)
{
	Vector v1 = (Vector)(p1 - p0), v2 = p2 - p0;
	if (cross(v1, v2) > EPS) return COUNTER_CLOCKWISE;
	if (cross(v1, v2) < -EPS) return CLOCKWISE;
	if (dot(v1, v2) < -EPS) return ONLINE_BACK;
	if (abs(v1) < abs(v2)) return ONLINE_FRONT;
	return ON_SEGMENT;
}

bool IsOrthogonal(const Vector &a, const Vector &b)
{
	return equals(dot(a, b), 0.0);
}
bool IsOrthogonal(const Line &l1, const Line &l2)
{
	return equals(dot(l1.b - l1.a, l2.b - l2.a), 0.0);
}

bool IsParallel(const Vector &a, const Vector &b)
{
	return equals(cross(a, b), 0.0);
}
bool IsParallel(const Line &l1, const Line &l2)
{
	return equals(cross(l1.b - l1.a, l2.b - l2.a), 0.0);
}

Point Project(const Line &s, const Point &p)
{
	Vector v1 = s.b - s.a;
	Vector v2 = p - s.a;
	double r = dot(v1, v2) / norm(v1);
	return s.a + v1 * r;
}

Point Reflect(const Line &s, const Point &p)
{
	return p + (Project(s, p) - p) * 2.0;
}

double GetDistance(const Line &l, const Point &p)
{
	return abs(cross(l.a - l.b, p - l.a) / abs(l.b - l.a));
}

double GetDistance(const Segment &s, const Point &p)
{
	if (dot(s.b - s.a, p - s.a) < 0.0) return abs(p - s.a);
	if (dot(s.a - s.b, p - s.b) < 0.0) return abs(p - s.b);
	return GetDistance(ToLine(s), p);
}

double GetDistance(const Segment &s1, const Segment &s2)
{
	if (Intersects(s1, s2)) return 0.0;
	return min(min(GetDistance(s1, s2.a), GetDistance(s1, s2.b)),
			   min(GetDistance(s2, s1.a), GetDistance(s2, s1.b)));
}

bool Intersects(const Line &l, const Segment &s)
{
	return CCW(l.a, l.b, s.a)*CCW(l.a, l.b, s.b) != 1;
}

bool Intersects(const Segment &l1, const Segment &l2)
{
	return CCW(l1.a, l1.b, l2.a)*CCW(l1.a, l1.b, l2.b) <= 0 &&
		CCW(l2.a, l2.b, l1.a)*CCW(l2.a, l2.b, l1.b) <= 0;
}

Point GetIntersection(const Line &l1, const Line &l2)
{
	Vector a1 = l1.b - l1.a, a2 = l2.b - l2.a;
	Vector b1 = l2.a - l1.a, b2 = l1.a - l2.b;
	double s1 = cross(a1, b1) / 2, s2 = cross(a1, b2) / 2;
	return Point(l2.a.real() + a2.real()*s1 / (s1 + s2), l2.a.imag() + a2.imag()*s1 / (s1 + s2));
}

vector<Point> GetIntersection(const Circle &c1, const Circle &c2)
{
	vector<Point> res;
	double d, a, t;
	d = sqrt(norm((c2.c - c1.c)));
	if (d < abs(c1.r - c2.r) || c1.r + c2.r < d) return res; //?????????0
	a = acos((c1.r*c1.r + d*d - c2.r*c2.r) / (2 * c1.r*d));
	t = atan2(c2.c.imag() - c1.c.imag(), c2.c.real() - c1.c.real());
	res.push_back(Point(c1.c.real() + c1.r*cos(t + a), c1.c.imag() + c1.r*sin(t + a)));
	res.push_back(Point(c1.c.real() + c1.r*cos(t - a), c1.c.imag() + c1.r*sin(t - a)));
	if (equals(res[0], res[1])) res.pop_back(); //?????????1
	return res;
}

int Contains(const Polygon &g, const Point &p)
{
	Line l = Line(p, Point(INF, p.imag()));
	int cnt = 0, n = g.size();
	for (int i = 0; i < n; i++)
	{
		Vector a = g[i] - p;
		Vector b = g[(i + 1) % n] - p;
		if (CCW(g[i], g[(i + 1) % n], p) == 0) return 2; //?????????
		if (a.imag() > b.imag()) swap(a, b);
		if (a.imag() <= EPS && EPS < b.imag() && cross(a, b) > EPS) cnt++;
	}
	if ((cnt & 1) == 1) return 1; //????????????
	return 0; //???????????????
}

Polygon AndrewScan(Polygon &s)
{
	if (s.size() <= 2) return s;
	sort(s.begin(), s.end(), comp);
	Polygon g;
	for (int i = 0; i < s.size(); i++)
	{
		//CCW?????¨?????????==COUNTER_CLOCKWISE???????????¨???????????????
		for (int n = g.size(); n >= 2 && CCW(g[n - 2], g[n - 1], s[i]) != CLOCKWISE; n--)
		{
			g.pop_back();
		}
		g.push_back(s[i]);
	}
	for (int i = s.size() - 2; i >= 0; i--)
	{
		for (int n = g.size(); CCW(g[n - 2], g[n - 1], s[i]) != CLOCKWISE; n--)
		{
			g.pop_back();
		}
		g.push_back(s[i]);
	}
	reverse(g.begin(), g.end());
	g.pop_back();
	return g;
}

Polygon ConvexCut(const Polygon &g, const Line &l)
{
	Polygon res;
	for (int i = 0; i < g.size(); i++)
	{
		Segment s = Segment(g[i], g[(i + 1) % g.size()]);
		if (CCW(l.a, l.b, s.a) != -1) res.push_back(s.a);
		if (CCW(l.a, l.b, s.a)*CCW(l.a, l.b, s.b) == -1)
			res.push_back(GetIntersection(l, ToLine(s)));
	}
	return res;
}


//Latte
double toRad(double a){return a/180.0*PI;}
double toDeg(double a){return a*180.0/PI;}
Point rotate(Point p,double d){
    return Point(p.real()*cos(d)-p.imag()*sin(d),p.real()*sin(d)+p.imag()*cos(d));
}

//Malta

struct UF{
    int N;
    vector<int>par,size;

    void init(int _N){
        N=_N;
        par.resize(N);
        size.resize(N,1);
        for(int i=0;i<N;i++)par[i]=i;
    }

    int find(int x){
        return x==par[x]?x:par[x]=find(par[x]);
    }

    void unite(int x,int y){
        x=find(x);y=find(y);
        if(x==y)return;
        if(size[x]<size[y])swap(x,y);
        par[y]=x;
        size[x]+=size[y];
    }

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


#define rep(i,n) for(int i=0;i<(n);i++)

int M;
vector<Polygon>vec;

bool isok(Polygon &g,Polygon &h){
    bool ret=false;
    rep(i,4){
        Segment s(g[i],g[(i+1)%4]);
        rep(j,4){
            Segment t(h[j],h[(j+1)%4]);
            if(Intersects(s,t))ret=true;
        }
    }
    rep(i,4){
        if(Contains(g,h[i]))ret=true;
        if(Contains(h,g[i]))ret=true;
    }
    return ret;
}

void solve(){
    scanf("%d",&M);
    vec.resize(M,Polygon(4));
    rep(i,M)rep(j,4)cin>>vec[i][j];

    UF uf;
    uf.init(M);

    rep(i,M)rep(j,M)if(isok(vec[i],vec[j]))uf.unite(i,j);

    int ans=0;
    rep(i,M)if(uf.find(i)==i)ans++;
    printf("%d\n",ans);
}

signed main()
{
    int N;
    while(scanf("%d",&N),N){
        rep(i,N)solve();
    }
	return 0;
}
accuracy: 1
time_ms: 30
memory_kb: 1304
final_rank: 1