Problem:
Problem G: 穴掘って埋まってます
Problem Statement
魔法使いの萩原さんは、非常にネガティブな性格である。彼女は、何か落ち込むようなことがあると、穴掘りの魔法を使って穴を作り、泣きながら埋まってしまう。穴掘りの魔法とは、次のようなものである。
彼女は、まずN本の線分を地面に描く。そして、彼女が呪文を唱えると、線で囲まれた範囲に穴が出来上がる。交差・内包の関係にある2つの穴は、1つの穴として数える。ただし、頂点のみで接している2つの穴は、別々の穴として数える。
図1と図2に、Sample Input 1と2の図を示す。まず、図1において、Aの三角形とBの三角形は、頂点のみで接しているため、別々の穴と数える。Bの三角形とCの三角形は、辺同士重なっているため、1つの穴として数える。よって、図1は、穴2つである。図2では、Dの四角形がEの四角形に包含されているため、これらは1つの穴と数える。Eは、右上の穴とも交差しているため、図2は、穴1つである。
あなたは、萩原さんの世話係であり、彼女が作った無数の穴を埋めなければならない。どれくらい穴を埋めないといけないのかを知るために、彼女が作った穴の数を求めるプログラムを作成せよ。
Input
各データセットは、以下の形式で入力される。
N
xa1 ya1 xb1 yb1
xa2 ya2 xb2 yb2
...
xaN yaN xbN ybN
入力は全て整数で表わされる。1行目は、線分の数である。続いてN行にわたって、線分の情報が与えられる。1つの線分は、線分の端点(xai, yai)と(xbi, ybi)によって表わされる。
Constraints
1 <= N <= 50
-1000 <= 座標 <= 1000
(xai, yai)と(xbi, ybi)の位置は異なる
2つの線分が交差しているとき、2つの線分の端点4つが一直線上に並ぶことはない
Output
穴の個数を1行で出力せよ。
Sample Input 1
8
5 0 5 10
5 10 0 5
0 5 5 0
10 0 10 10
10 10 5 5
5 5 10 0
10 2 15 5
15 5 10 8
Output for the Sample Input 1
2
Sample Input 2
11
0 0 10 0
10 0 10 10
10 10 0 10
0 10 0 0
1 2 9 2
8 1 8 9
9 8 1 8
2 9 2 1
8 11 11 8
8 11 14 10
11 8 10 14
Output for the Sample Input 2
1

Submitted solutions:

code_snippet: #include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<iomanip>
#include<math.h>
#include<complex>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<functional>
#include<assert.h>
#include<numeric>
using namespace std;
#define REP(i,m,n) for(int i=(int)(m) ; i < (int) (n) ; ++i )
#define rep(i,n) REP(i,0,n)
using ll = long long;
const int inf=1e9+7;
const ll longinf=1LL<<60 ;
const ll mod=1e9+7 ;

using ld = long double;
using Point =  complex<ld>;
const ld eps = 1e-12;
const ld pi = acos(-1.0);

bool eq(ld a, ld b) {
	return (abs(a - b) < eps);
}

bool cmp(Point x,Point y){
	if(eq(x.real(),y.real()))return x.imag()<y.imag();
	return x.real()<y.real();
}

bool eqq(Point x,Point y){
	return eq(x.real(),y.real())&&eq(x.imag(),y.imag());
}
//内積
ld dot(Point a, Point b) {
	return real(conj(a)*b);
}
//外積
ld cross(Point a, Point b) {
	return imag(conj(a)*b);
}


//線分
//直線にするなら十分通い２点を端点とすればよい
class Line {
public:
	Point a, b;
};
//円
class Circle {
public:
	Point p;
	ld r;
};
//3点の位置関係
int ccw(Point a, Point b, Point c) {
	b -= a; c -= a;
	if (cross(b, c) > eps)return 1;//a,b,cが反時計回り
	if (cross(b, c) < -eps)return -1;//a,b,cが時計回り
	if (dot(b, c) < 0)return 2;//c,a,bの順に一直線
	if (norm(b) < norm(c))return -2;//a,b,cの順に一直線
	return 0;//a,c,bの順に一直線
}
//2直線の交差判定
bool isis_ll(Line l, Line m) {
	return !eq(cross(l.b - l.a, m.b - m.a), 0);
}
//直線と線分の交差判定
bool isis_ls(Line l, Line s) {
	return (cross(l.b - l.a, s.a - l.a)*cross(l.b - l.a, s.b - l.a) < eps);
}
//点が直線上に存在するか
bool isis_lp(Line l, Point p) {
	return (abs(cross(l.b - p, l.a - p)) < eps);
}
//点が線分上に存在するか
bool isis_sp(Line s, Point p) {
	return (abs(s.a - p) + abs(s.b - p) - abs(s.b - s.a) < eps);
}
//線分と線分の交差判定
bool isis_ss(Line s, Line t) {
	if (isis_sp(s, t.a) || isis_sp(s, t.b) || isis_sp(t, s.a) || isis_sp(t, s.b))return true;
	return(cross(s.b - s.a, t.a - s.a)*cross(s.b - s.a, t.b - s.a) < -eps && cross(t.b - t.a, s.a - t.a)*cross(t.b - t.a, s.b - t.a) < -eps);
}
//点から直線への垂線の足
Point proj(Line l, Point p) {
	ld t = dot(p - l.a, l.a - l.b) / norm(l.a - l.b);
	return l.a + t * (l.a - l.b);
}
//直線と直線の交点
//平行な２直線に対しては使うな！！！！
Point is_ll(Line s, Line t) {
	Point sv = s.b - s.a; Point tv = t.b - t.a;
	return s.a + sv * cross(tv, t.a - s.a) / cross(tv, sv);
}
//直線と点の距離
ld dist_lp(Line l, Point p) {
	return abs(p - proj(l, p));
}
//直線と直線の距離
ld dist_ll(Line l, Line m) {
	return isis_ll(l, m) ? 0 : dist_lp(l, m.a);
}
//線分と直線の距離
ld dist_ls(Line l, Line s) {
	return isis_ls(l, s) ? 0 : min(dist_lp(l, s.a), dist_lp(l, s.b));
}
//線分と点の距離
ld dist_sp(Line s, Point p) {
	Point r = proj(s, p);
	return isis_sp(s, r) ? abs(p - r) : min(abs(p - s.a), abs(p - s.b));
}
//線分と線分の距離
ld dist_ss(Line s, Line t) {
	if (isis_ss(s, t))return 0;
	return min({ dist_sp(s,t.a),dist_sp(s,t.b),dist_sp(t,s.a),dist_sp(t,s.b) });
}

bool included(Point p,vector<Point>& pol){
    int n=pol.size();
    if(n<=2)return false;
    ld sum=0;
    rep(i,n-1){
        if(isis_sp(Line{pol[i],pol[i+1]},p))return false;
    }
    rep(i,n-1){
        sum+=arg((pol[i+1]-p)/(pol[i]-p));
    }
    return abs(sum)>eps;
}
//オーバーラップする線分があると壊れる
vector<vector<int>> segmentArrangement(const vector<Line>& segs, vector<Point>& ps) {
	int n = segs.size();
	rep (i, n) {
		ps.push_back(segs[i].a);
		ps.push_back(segs[i].b);
		rep (j, i) {
			if (isis_ss(segs[i],segs[j])){
	    		ps.push_back(is_ll(segs[i], segs[j]));
			}
    	}
	}
	sort(ps.begin(), ps.end(),cmp);
	ps.erase(unique(ps.begin(), ps.end(),eqq), ps.end());

	int m = ps.size();
	vector<vector<int>> gr(m);
	vector<pair<ld, int>> list;
	rep (i, n) {
		list.clear();	
		rep (j, m) {
			if (isis_sp(segs[i], ps[j]))list.push_back(make_pair(abs(segs[i].a-ps[j]), j));
		}
		sort(list.begin(), list.end());
		rep (j, list.size() - 1) {
			int a = list[j  ].second;
			int b = list[j+1].second;
			gr[a].emplace_back(b);
			gr[b].emplace_back(a);
		}
	}
	return gr;
}

struct LowLink {
    const vector<vector<int>> &g;
    vector< int > used, ord, low;
    vector< int > articulation;
    vector< pair< int, int > > bridge;
 
    LowLink(const vector<vector<int>> &g) : g(g) {}
 
    int dfs(int idx, int k, int par) {
        used[idx] = true;
        ord[idx] = k++;
        low[idx] = ord[idx];
        bool is_articulation = false;
        int cnt = 0;
        for(auto &to : g[idx]) {
            if(!used[to]) {
                ++cnt;
                k = dfs(to, k, idx);
                low[idx] = min(low[idx], low[to]);
                if(par!=-1&&low[to]>=ord[idx])is_articulation =true;
                if(ord[idx] < low[to]) bridge.emplace_back(minmax(idx, (int) to));
            }
            else if(to != par) {
                low[idx] = min(low[idx], ord[to]);
            }
        }
        if(par==-1&&cnt>1)is_articulation= true;
        if(is_articulation) articulation.push_back(idx);
        return k;
    }
 
    virtual void build() {
        used.assign(g.size(), 0);
        ord.assign(g.size(), 0);
        low.assign(g.size(), 0);
        int k = 0;
        for(int i = 0; i < g.size(); i++) {
            if(!used[i]) k = dfs(i, k, -1);
        }
    }
};

map<pair<int,int>,int> edges;
struct BiConnectedComponents : LowLink {
    using LL = LowLink;
 
    vector< int > used;
    vector< vector< pair< int, int > > > bc;
    vector< pair< int, int > > tmp;
 
    BiConnectedComponents(const vector<vector<int>> &g) : LL(g) {}
 
    void dfs(int idx, int par,int& k) {
        used[idx] = true;
        for(auto &to : this->g[idx]) {
            if(to == par) continue;
            if(!used[to] || this->ord[to] < this->ord[idx]) {
                tmp.emplace_back(minmax(idx, to));
            }
            if(!used[to]) {
                dfs(to, idx, k);
                if(this->low[to] >= this->ord[idx]) {
                    bc.emplace_back();
                    for(;;) {
                        auto e = tmp.back();
                        edges[e]=k;
                        edges[{e.second, e.first}]=k;
                        bc.back().emplace_back(e);
                        tmp.pop_back();
                        if(e.first == min(idx, to) && e.second == max(idx, to)) {
                        break;
                        }
                    }
                    k++;
                }
            }
        }
    }
 
    void build(int& k) {
        LL::build();
        used.assign(this->g.size(), 0);
        k = 0;
        for(int i = 0; i < used.size(); i++) {
            if(!used[i]) {
                dfs(i, -1, k);
            }
        }
    }
};



int main(){
    int n;
	cin>>n;
	vector<Line> segs(n);
	rep(i,n){
		double x,y,xx,yy;
		cin>>x>>y>>xx>>yy;
		segs[i] = Line{Point(x,y),Point(xx,yy)};
	}
	vector<Point> ps;
	auto G = segmentArrangement(segs,ps);
	n=G.size();
    BiConnectedComponents bc(G);
    int m=0;
    bc.build(m);
    vector<int> ans(m);
    for(auto g : bc.bc){
        if(g.size()>=3)ans[edges[g[0]]]++;
    }
    map<int,int> idx[n];
    vector<int> used[n];
    rep(i,n){
        sort(G[i].begin(),G[i].end(),[&](int x,int y){
            return arg(ps[x]-ps[i])>arg(ps[y]-ps[i]);
        });
        rep(j,G[i].size()){
            idx[i][G[i][j]]=j;
        }
        used[i].resize(G[i].size());
    }
    vector<vector<Point>> loops[m];
    
    rep(i,n){
        rep(j,G[i].size()){
            if(used[i][j])continue;
            used[i][j]=true;
            int pre=i,cur=G[i][j];
            vector<Point> loop;
            loop.push_back(ps[i]);
            loop.push_back(ps[cur]);
            while(cur!=i){
                int nxt=(idx[cur][pre]+1)%G[cur].size();
                while(used[cur][nxt]||edges[{pre,cur}]!=edges[{cur,G[cur][nxt]}])nxt=(nxt+1)%G[cur].size();
                used[cur][nxt]=true;
                pre=cur;
                cur=G[cur][nxt];
                loop.push_back(ps[cur]);
            }
            loops[edges[{pre,cur}]].push_back(loop);
        }
    }
    vector<int> checked(m);
    rep(i,n){
        
        rep(j,m){
            for(auto pol : loops[j]){
                if(included(ps[i],pol)){
                    for(auto to : G[i]){
                        if(edges[{i,to}]!=j){
                            ans[edges[{i,to}]]=0;
                        }
                    }
                }
            }
        }
    }
    int Ans=0;
    rep(i,m)Ans+=ans[i];
    cout<<Ans<<endl;

    return 0;
}
accuracy: 1
time_ms: 140
memory_kb: 3708
final_rank: 8

code_snippet: #include <cassert>// c
#include <ctime>
#include <iostream>// io
#include <iomanip>
#include <fstream>
#include <sstream>
#include <vector>// container
#include <map>
#include <set>
#include <queue>
#include <bitset>
#include <stack>
#include <algorithm>// other
#include <complex>
#include <numeric>
#include <functional>
#include <random>
#include <regex>

using namespace std;
typedef long long ll;

#define ALL(c) (c).begin(),(c).end()
#define FOR(i,l,r) for(int i=(int)l;i<(int)r;++i)
#define REP(i,n) FOR(i,0,n)
#define FORr(i,l,r) for(int i=(int)r-1;i>=(int)l;--i)
#define REPr(i,n) FORr(i,0,n)
#define EACH(i,c) for(__typeof((c).begin()) i=(c).begin(); i!=(c).end(); ++i)
#define IN(l,v,r) ((l)<=(v) && (v)<(r))
#define UNIQUE(v) v.erase(unique(ALL(v)),v.end())
//debug
#define DUMP(x)  cerr << #x << " = " << (x)
#define LINE()    cerr<< " (L" << __LINE__ << ")"

template<typename T,typename U> T pmod(T x,U M){return (x%M+M)%M;}

stringstream ss;

// must template
typedef long double D;
const D INF = 1e12,EPS = 1e-8;

typedef complex<D> P;
#define X real()
#define Y imag()
istream& operator >> (istream& is,complex<D>& p){
    D x,y;is >> x >> y;p=P(x,y);return is;
}

int sig(D a,D b=0){return a<b-EPS?-1:a>b+EPS?1:0;}
template<typename T> bool eq(const T& a,const T& b){return sig(abs(a-b))==0;}
bool compX (const P& a,const P& b){return !eq(a.X,b.X)?sig(a.X,b.X)<0:sig(a.Y,b.Y)<0;}
namespace std{
    bool operator <  (const P& a,const P& b){return compX(a,b);}
    bool operator == (const P& a,const P& b){return eq(a,b);}
};
// a×b
D cross(const P& a,const P& b){return imag(conj(a)*b);}
// a・b
D dot(const P& a,const P& b) {return real(conj(a)*b);}
int ccw(const P& a,P b,P c){
    b -= a; c -= a;
    if (cross(b,c) > EPS)   return +1;       // counter clockwise
    if (cross(b,c) < -EPS)   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; //a--c--b on line (c==b,c==a)
}
// //must template
//直線・線分
struct L : public vector<P> {
    P vec() const {return this->at(1)-this->at(0);}
    L(const P &a, const P &b){push_back(a); push_back(b);}
    L(){push_back(P());push_back(P());}
};
istream& operator >> (istream& is,L& l){P s,t;is >> s >> t;l=L(s,t);return is;}

bool isIntersectLL(const L &l, const L &m) {
    return sig(cross(l.vec(), m.vec()))!=0 || // non-parallel
    sig(cross(l.vec(), m[0]-l[0])) ==0;   // same line
}
bool isIntersectLS(const L &l, const L &s) {
    return sig(cross(l.vec(), s[0]-l[0])*       // s[0] is left of l
        cross(l.vec(), s[1]-l[0]))<=0; // s[1] is right of l
}
bool isIintersectLP(const L &l, const P &p) {return sig(cross(l[1]-p, l[0]-p))==0;}

// verified by ACAC003 B
// http://judge.u-aizu.ac.jp/onlinejudge/creview.jsp?rid=899178&cid=ACAC003
bool isIntersectSS(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 isIntersectSP(const L &s, const P &p) {
    return sig(abs(s[0]-p)+abs(s[1]-p),abs(s[1]-s[0])) <=0; // triangle inequality
}

// 交点計算
// verified by AOJLIB
// http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=1092231
P crosspoint(const L &l, const L &m) {
    D A = cross(l.vec(), m.vec());
    D B = cross(l.vec(), l[1] - m[0]);
    if (sig(A)==0 && sig(B)==0) return m[0]; // same line
    if (sig(A)==0) assert(false); //交点を持たない．
    return m[0] + B / A * (m[1] - m[0]);
}

typedef D Cost; Cost CINF=1e20;
struct Edge{
    int f,t;Cost c;D th;
    Edge(int f,int t,Cost c,D th) : f(f),t(t),c(c),th(th){
    };
    bool operator<(const Edge& r) const{return th<r.th;}
};
typedef vector<vector<Edge> >  Graph;

Graph segmentArrangement(vector<L> ss, vector<P> &ps) {
    vector<vector<D>> vs(ss.size());
    REP(i,ss.size()){vs[i].push_back(0);vs[i].push_back(abs(ss[i][1]-ss[i][0]));}
    REP(i,ss.size())REP(j,ps.size())if(isIntersectSP(ss[i], ps[j]))
        vs[i].push_back(abs(ps[j]-ss[i][0]));
    
    REP(i,ss.size())FOR(j,i+1,ss.size()){
        L& a=ss[i],b=ss[j];
        if(isIntersectSS(a,b)){
            P p = crosspoint(a,b);
            vs[i].push_back(abs(p-a[0]));vs[j].push_back(abs(p-b[0]));              
        }
    }
    REP(i,vs.size()){sort(ALL(vs[i]));vs[i].erase(unique(ALL(vs[i]),eq<D>),vs[i].end());}
    REP(i,vs.size())REP(j,vs[i].size())ps.push_back(ss[i][0]+vs[i][j]*ss[i].vec()/abs(ss[i].vec()));
    sort(ALL(ps));UNIQUE(ps);

    Graph g(ps.size());
    REP(i,vs.size()){
        vector<D>& cut=vs[i];
        REP(j,cut.size()-1){
            P a = ss[i][0] + vs[i][j]*ss[i].vec()/abs(ss[i].vec()),b = ss[i][0] + vs[i][j+1]*ss[i].vec()/abs(ss[i].vec());
            int f=distance(ps.begin(),lower_bound(ALL(ps),a)), t=distance(ps.begin(),lower_bound(ALL(ps),b));
            g[f].emplace_back(f,t,abs(b-a),arg(b-a));
            g[t].emplace_back(t,f,abs(a-b),arg(a-b));
        }
    }
    return g;
}

typedef vector<P> Poly,ConvexPoly;

enum { OUT, ON, IN };
int contains(const Poly& ps, const P& p) {
    bool in = false;int n=ps.size();
    REP(i,n){
        P a = ps[i] - p, b = ps[pmod(i+1,n)] - p;
        if (a.Y > b.Y) swap(a, b);
        if (a.Y <= 0 && 0 < b.Y)
        if (cross(a, b) < 0) in = !in;
            if (cross(a, b) == 0 && dot(a, b) <= 0) return ON;
    }
    return in ? IN : OUT;
}

vector<Poly> find_polys(const vector<P>& ps,Graph& g){
    const int n=g.size();
    vector<vector<bool>> used(n);
    REP(i,n)used[i]=vector<bool>(g[i].size());
    
    REP(i,n)sort(ALL(g[i]));

    vector<Poly> res;
    REP(i,n)REP(j,g[i].size()){
        int v=i,e_idx=j;
        Poly qs;
        while(true){
            if(used[v][e_idx])break;
            used[v][e_idx] =true;
            Edge& e=g[v][e_idx];
            qs.push_back(ps[v]);
            REP(k,g[e.t].size())if(g[e.t][k].t==v){
                e_idx =(k+1)%g[e.t].size();
                v=e.t;
                break;
            }
        }
        if(qs.size()>=3)res.push_back(move(qs));
    }

    // cerr << res.size() <<endl;

    //remove ccw poly
    int idx = 0;
    while(idx < res.size()) {
        const P diff = res[idx][1] - res[idx][0];
        const P p = res[idx][0] + diff * 0.5L + diff * P(0,-1) * (2.0L * EPS /abs(diff));
        if(contains(res[idx], p))++idx;
        else res.erase(res.begin() + idx);
    }
    return res;
}

struct UnionFind{
    vector<int> par,rank,ss;int size;
    UnionFind(int n){
        REP(i,n) par.push_back(i);
        rank = vector<int>(n);ss=vector<int>(n,1);size=n;
    }
    int root(int x){
        if(par[x] == x)return x;
        return par[x] = root(par[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]){
            par[x] = y;ss[y]+=ss[x];
        }else{
            par[y] = x;ss[x]+=ss[y];
        }
        if(rank[x] == rank[y]) rank[x]++;
        size--;
    }
    int getS(int x){
        return ss[root(x)];
    }
};

int main(){
    cin.tie(0); ios::sync_with_stdio(false);
    ss   <<fixed <<setprecision(20);
    cout <<fixed <<setprecision(20);
    cerr <<fixed <<setprecision(20);

    int N;cin >>N;
    vector<L> ls(N);REP(i,N) cin >> ls[i];
    vector<P> ps;
    Graph g=segmentArrangement(ls,ps);
    vector<Poly>  face=find_polys(ps,g);
    // cerr << ps.size() <<endl;
    // cerr << face.size() <<endl;
    UnionFind uf(face.size());
    REP(i,face.size()){
        // cerr << i <<endl;
        // REP(l,face[i].size())cerr << face[i][l] << endl;
       
        REP(j,face.size())if(i!=j){
            bool isin=true;
            REP(k,face[j].size())
                if(contains(face[i],face[j][k])!=IN)isin=false;
            
            bool iscross=false;
            REP(k,face[j].size())REP(l,face[i].size()){
                L l1(face[j][k],face[j][pmod(k+1,face[j].size())]);
                L l2(face[i][l],face[i][pmod(l+1,face[i].size())]);

                if(isIntersectSS(l1,l2) && sig(cross(l1.vec(),l2.vec()))==0
                    && max(l1[0],l1[1])!= min(l2[0],l2[1])
                    && max(l2[0],l2[1])!= min(l1[0],l1[1])){
                    // cerr <<"!!"<<endl;
                    // cerr << l1[0]<<" " << l1[1] <<endl;
                    // cerr << l2[0]<<" " << l2[1] <<endl;
                    iscross=true;
                }
            }
            // cerr << i <<" " << j <<endl;
            // cerr << isin <<" " << iscross <<endl;
            if(isin || iscross)uf.unite(i,j);
        }
    }
    cout << uf.size <<endl;

    return 0;
 }
accuracy: 1
time_ms: 480
memory_kb: 1548
final_rank: 9

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

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; }
double norm(Point a){ return a.x*a.x+a.y*a.y; }
double abs(Point a){ return sqrt(norm(a)); }

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) { return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; }

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
bool ccwtest(Point p,Point q,Point r){ return cross3p(p,q,r) > 0; }
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}

double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=-1,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size())
    REP(j,i+1,vs.size())
    if(intersectSS(vs[i],vs[j]))
      ps.push_back(Point(crosspoint(vs[i],vs[j])));

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  

  vector<Point> tmp;
  rep(i,ps.size()) if( ret[i].size() >= 2 ) tmp.push_back(ps[i]);
  ps = tmp;
  ret.clear();
  ret.resize(ps.size());
  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }    

  return ret;
}

////////////////// にゃんこまっしぐら ///////////////////////////////
const int MAX_V = 5000;
bool visited[MAX_V][MAX_V];
vector<vector<Edge> > G;
vector<Point> vp;

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &used){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
  if( used.count(vp[node]) ) return visited[node][edge] = false;
  used.insert(vp[node]);
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,used)) return true;
  else return visited[node][edge] = false;
}
///////////////// みゃおみゃお //////////////////////////////////////

int uf[MAX_V];
void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }
int find(int x) {
  if( x == uf[x] ) return x;
  return uf[x] = find(uf[x]);
}
void unit(int x,int y){
  x = find(x), y = find(y);
  if( x != y ) uf[x] = y;
}

bool  LT(double a,double b) { return !equals(a,b) && a < b; }
bool LTE(double a,double b) { return  equals(a,b) || a < b; }

Point vir;
bool comp_angle(const Edge &a, const Edge &b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

bool inPolygon(Polygon a,Polygon b) {
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
    if( !inPolygon(b,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) rep(j,(int)b.size()) {
    Segment seg_a = Segment(a[i],a[(i+1)%(int)a.size()]);
    Segment seg_b = Segment(b[j],b[(j+1)%(int)b.size()]);
    if( equals(cross(seg_a.p1-seg_a.p2,seg_b.p1-seg_b.p2),0.0) ) {
      if( onSegment(seg_a.p1,seg_a.p2,seg_b.p1) || onSegment(seg_a.p1,seg_a.p2,seg_b.p2) ||
          onSegment(seg_b.p1,seg_b.p2,seg_a.p1) || onSegment(seg_b.p1,seg_b.p2,seg_a.p2) ) {
        vector<Point> tmp;
        tmp.push_back(seg_a.p1), tmp.push_back(seg_a.p2);
        tmp.push_back(seg_b.p1), tmp.push_back(seg_b.p2);
        sort(tmp.begin(),tmp.end());
        if( tmp[1] == tmp[2] ) continue;
        return true;
      }
    }
  }
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
      if( inPolygon(b,mp) ) return true;
  }
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
      if( inPolygon(a,mp) ) return true;
  }
  return false;
}

int N;

void compute(vector<Segment> &segs){
  G = segmentArrangement(segs,vp);
  int V = G.size();

  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }

  rep(i,V) rep(j,(int)G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,(int)G[to].size()) if( G[to][k].to == i ) {
      G[i][j].rev  = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> polies;
  rep(i,V) rep(j,(int)G[i].size()) if( !visited[i][j] ) {
    vector<Point> buf;
    set<Point> used;
    if( getPath(i,i,j,buf,used) ) if( (int)buf.size() >= 3 ) polies.push_back(buf);
  }
  V = polies.size();
  init(V);
  set<int> S;
  rep(i,V) rep(j,V) if( i != j ) {
    if( canUnit(polies[i],polies[j]) ) unit(i,j);
  }
  rep(i,V) S.insert(find(i));
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  vector<Segment> segs(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute(segs);
  return 0;
}
accuracy: 1
time_ms: 360
memory_kb: 2956
final_rank: 10

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=-1,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size())
    REP(j,i+1,vs.size())
    if(intersectSS(vs[i],vs[j]))
      ps.push_back(Point(crosspoint(vs[i],vs[j])));

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  

  vector<Point> tmp;
  rep(i,ps.size()) if( ret[i].size() >= 2 ) tmp.push_back(ps[i]);
  ps = tmp;
  ret.clear();
  ret.resize(ps.size());
  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }    

  return ret;
}

////////////////// にゃんこまっしぐら ///////////////////////////////
const int MAX_V = 5000;
bool visited[MAX_V][MAX_V];
vector<vector<Edge> > G;
vector<Point> vp;

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &used){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
  if( used.count(vp[node]) ) return visited[node][edge] = false;
  used.insert(vp[node]);
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,used)) return true;
  else return visited[node][edge] = false;
}
///////////////// みゃおみゃお //////////////////////////////////////

int uf[MAX_V];
void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }
int find(int x) {
  if( x == uf[x] ) return x;
  return uf[x] = find(uf[x]);
}
void unit(int x,int y){
  x = find(x), y = find(y);
  if( x != y ) uf[x] = y;
}

bool  LT(double a,double b) { return !equals(a,b) && a < b; }
bool LTE(double a,double b) { return  equals(a,b) || a < b; }

// true -> CCW, false -> CW
bool rotationCheck(vector<Point>& vec){
  double sum = 0;
  for(int i=0;i<vec.size();i++) sum += cross(vec[i],vec[(i+1)%vec.size()]);
  return ( !equals(sum,0.0) && sum > 0 ); 
}

Point vir;
bool comp_angle(const Edge &a, const Edge &b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

bool inPolygon(Polygon a,Polygon b) {
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
    if( !inPolygon(b,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) rep(j,(int)b.size()) {
    Segment seg_a = Segment(a[i],a[(i+1)%(int)a.size()]);
    Segment seg_b = Segment(b[j],b[(j+1)%(int)b.size()]);
    if( equals(cross(seg_a.p1-seg_a.p2,seg_b.p1-seg_b.p2),0.0) ) {
      if( onSegment(seg_a.p1,seg_a.p2,seg_b.p1) || onSegment(seg_a.p1,seg_a.p2,seg_b.p2) ||
          onSegment(seg_b.p1,seg_b.p2,seg_a.p1) || onSegment(seg_b.p1,seg_b.p2,seg_a.p2) ) {
        vector<Point> tmp;
        tmp.push_back(seg_a.p1), tmp.push_back(seg_a.p2);
        tmp.push_back(seg_b.p1), tmp.push_back(seg_b.p2);
        sort(tmp.begin(),tmp.end());
        if( tmp[1] == tmp[2] ) continue;
        return true;
      }
    }
  }
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
      if( inPolygon(b,mp) ) return true;
  }
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
      if( inPolygon(a,mp) ) return true;
  }
  return false;
}

int N;

void compute(vector<Segment> &segs){
  G = segmentArrangement(segs,vp);
  int V = G.size();

  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }

  rep(i,V) rep(j,(int)G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,(int)G[to].size()) if( G[to][k].to == i ) {
      G[i][j].rev  = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> polies;
  rep(i,V) rep(j,(int)G[i].size()) if( !visited[i][j] ) {
    vector<Point> buf;
    set<Point> used;
    if( getPath(i,i,j,buf,used) ) if( (int)buf.size() >= 3 && !rotationCheck(buf) ) {
        polies.push_back(buf);
      }
  }
  V = polies.size();
  init(V);
  set<int> S;
  rep(i,V) rep(j,V) if( i != j ) {
    if( canUnit(polies[i],polies[j]) ) unit(i,j);
  }
  rep(i,V) S.insert(find(i));
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  vector<Segment> segs(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute(segs);
  return 0;
}
accuracy: 0.7
time_ms: 360
memory_kb: 2960
final_rank: 14

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=-1,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size())
    REP(j,i+1,vs.size())
    if(intersectSS(vs[i],vs[j]))
      ps.push_back(Point(crosspoint(vs[i],vs[j])));

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  

  vector<Point> tmp;
  rep(i,ps.size()) if( ret[i].size() >= 2 ) tmp.push_back(ps[i]);
  ps = tmp;
  ret.clear();
  ret.resize(ps.size());
  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }    

  return ret;
}

////////////////// にゃんこまっしぐら ///////////////////////////////
const int MAX_V = 5000;
bool visited[MAX_V][MAX_V];
vector<vector<Edge> > G;
vector<Point> vp;

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &used){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
  if( used.count(vp[node]) ) return visited[node][edge] = false;
  used.insert(vp[node]);
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,used)) return true;
  else return visited[node][edge] = false;
}
///////////////// みゃおみゃお //////////////////////////////////////

int uf[MAX_V];
void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }
int find(int x) {
  if( x == uf[x] ) return x;
  return uf[x] = find(uf[x]);
}
void unit(int x,int y){
  x = find(x), y = find(y);
  if( x != y ) uf[x] = y;
}

bool  LT(double a,double b) { return !equals(a,b) && a < b; }
bool LTE(double a,double b) { return  equals(a,b) || a < b; }

// true -> CCW, false -> CW
bool rotationCheck(vector<Point>& vec){
  double sum = 0;
  for(int i=0;i<vec.size();i++) sum += cross(vec[i],vec[(i+1)%vec.size()]);
  return ( !equals(sum,0.0) && sum > 0 ); 
}


Point vir;
bool comp_angle(const Edge &a, const Edge &b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}


bool inPolygon(Polygon a,Polygon b) {
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
    if( !inPolygon(b,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) rep(j,(int)b.size()) {
    Segment seg_a = Segment(a[i],a[(i+1)%(int)a.size()]);
    Segment seg_b = Segment(b[j],b[(j+1)%(int)b.size()]);
    if( equals(cross(seg_a.p1-seg_a.p2,seg_b.p1-seg_b.p2),0.0) ) {
      if( onSegment(seg_a.p1,seg_a.p2,seg_b.p1) || onSegment(seg_a.p1,seg_a.p2,seg_b.p2) ||
          onSegment(seg_b.p1,seg_b.p2,seg_a.p1) || onSegment(seg_b.p1,seg_b.p2,seg_a.p2) ) {
        vector<Point> tmp;
        tmp.push_back(seg_a.p1), tmp.push_back(seg_a.p2);
        tmp.push_back(seg_b.p1), tmp.push_back(seg_b.p2);
        sort(tmp.begin(),tmp.end());
        if( tmp[1] == tmp[2] ) continue;
        return true;
      }
    }
  }
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
      if( inPolygon(b,mp) ) return true;
  }
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
      if( inPolygon(a,mp) ) return true;
  }
  return false;
}

int N;

void compute(vector<Segment> &segs){
  G = segmentArrangement(segs,vp);
  int V = G.size();
  /*
  cout << V << endl;
  rep(i,V) cout << vp[i] << endl;
  */
  assert( V < MAX_V );



  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }

  rep(i,V) rep(j,(int)G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,(int)G[to].size()) if( G[to][k].to == i ) {
      G[i][j].rev  = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> polies;
  rep(i,V) rep(j,(int)G[i].size()) if( !visited[i][j] ) {
    vector<Point> buf;
    set<Point> used;
    if( getPath(i,i,j,buf,used) ) if( (int)buf.size() >= 3 ) {
        polies.push_back(buf);
      }
  }
  V = polies.size();
  /*
  cout << V << endl;
  rep(i,V){
    cout << i << "-th:" << endl;
    rep(j,polies[i].size()) cout << polies[i][j] << " "; cout << endl;
  }

  cout << endl;
  cout << endl;
  rep(i,V){
    rep(j,polies[i].size()){
      cout << polies[i][j].x << " " << polies[i][j].y << " " << polies[i][(j+1)%polies[i].size()].x << " " << polies[i][(j+1)%polies[i].size()].y << endl;
    }
  }
  cout << endl;
  cout << endl;
  */
  init(V);
  set<int> S;
  rep(i,V) rep(j,V) if( i != j ) {
    if( canUnit(polies[i],polies[j]) ) unit(i,j);
  }
  rep(i,V) S.insert(find(i));
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  vector<Segment> segs(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute(segs);
  return 0;
}
accuracy: 1
time_ms: 360
memory_kb: 2960
final_rank: 11

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-10)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=-1,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size())
    REP(j,i+1,vs.size())
    if(intersectSS(vs[i],vs[j]))
      ps.push_back(Point(crosspoint(vs[i],vs[j])));

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}

////////////////// にゃんこまっしぐら ///////////////////////////////
const int MAX_V = 5000;
bool visited[MAX_V][MAX_V];
vector<vector<Edge> > G;
vector<Point> vp;

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &used){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
  if( used.count(vp[node]) ) return visited[node][edge] = false;
  used.insert(vp[node]);
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,used)) return true;
  else return visited[node][edge] = false;
}
///////////////// みゃおみゃお //////////////////////////////////////

int uf[MAX_V];
void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }
int find(int x) {
  if( x == uf[x] ) return x;
  return uf[x] = find(uf[x]);
}
void unit(int x,int y){
  x = find(x), y = find(y);
  if( x != y ) uf[x] = y;
}

bool  LT(double a,double b) { return !equals(a,b) && a < b; }
bool LTE(double a,double b) { return  equals(a,b) || a < b; }

// true -> CCW, false -> CW
bool rotationCheck(vector<Point>& vec){
  double sum = 0;
  for(int i=0;i<vec.size();i++) sum += cross(vec[i],vec[(i+1)%vec.size()]);
  return ( !equals(sum,0.0) && sum > 0 ); 
}


Point vir;
bool comp_angle(const Edge &a, const Edge &b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}


bool inPolygon(Polygon a,Polygon b) {
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
    if( !inPolygon(b,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) rep(j,(int)b.size()) {
    Segment seg_a = Segment(a[i],a[(i+1)%(int)a.size()]);
    Segment seg_b = Segment(b[j],b[(j+1)%(int)b.size()]);
    if( equals(cross(seg_a.p1-seg_a.p2,seg_b.p1-seg_b.p2),0.0) ) {
      if( onSegment(seg_a.p1,seg_a.p2,seg_b.p1) || onSegment(seg_a.p1,seg_a.p2,seg_b.p2) ||
          onSegment(seg_b.p1,seg_b.p2,seg_a.p1) || onSegment(seg_b.p1,seg_b.p2,seg_a.p2) ) {
        vector<Point> tmp;
        tmp.push_back(seg_a.p1), tmp.push_back(seg_a.p2);
        tmp.push_back(seg_b.p1), tmp.push_back(seg_b.p2);
        sort(tmp.begin(),tmp.end());
        if( tmp[1] == tmp[2] ) continue;
        return true;
      }
    }
  }
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
      if( inPolygon(b,mp) ) return true;
  }
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
      if( inPolygon(a,mp) ) return true;
  }
  return false;
}

int N;

void compute(vector<Segment> &segs){
  G = segmentArrangement(segs,vp);
  int V = G.size();

  assert( V < MAX_V );

  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }

  rep(i,V) rep(j,(int)G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,(int)G[to].size()) if( G[to][k].to == i ) {
      G[i][j].rev  = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> polies;
  rep(i,V) rep(j,(int)G[i].size()) if( !visited[i][j] ) {
    vector<Point> buf;
    set<Point> used;
    if( getPath(i,i,j,buf,used) ) if( (int)buf.size() >= 3 ) {
        polies.push_back(buf);
      }
  }
  V = polies.size();
  /*
  cout << V << endl;
  rep(i,V){
    cout << i << "-th:" << endl;
    rep(j,polies[i].size()) cout << polies[i][j] << " "; cout << endl;
  }
  */
  init(V);
  set<int> S;
  rep(i,V) rep(j,V) if( i != j ) {
    if( canUnit(polies[i],polies[j]) ) unit(i,j);
  }
  rep(i,V) S.insert(find(i));
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  vector<Segment> segs(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute(segs);
  return 0;
}
accuracy: 0.7
time_ms: 350
memory_kb: 2968
final_rank: 13

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=-1,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size())
    REP(j,i+1,vs.size())
    if(intersectSS(vs[i],vs[j]))
      ps.push_back(Point(crosspoint(vs[i],vs[j])));

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}

////////////////// にゃんこまっしぐら ///////////////////////////////
const int MAX_V = 5000;
bool visited[MAX_V][MAX_V];
vector<vector<Edge> > G;
vector<Point> vp;

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &used){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
  if( used.count(vp[node]) ) return visited[node][edge] = false;
  used.insert(vp[node]);
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,used)) return true;
  else return visited[node][edge] = false;
}
///////////////// みゃおみゃお //////////////////////////////////////

int uf[MAX_V];
void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }
int find(int x) {
  if( x == uf[x] ) return x;
  return uf[x] = find(uf[x]);
}
void unit(int x,int y){
  x = find(x), y = find(y);
  if( x != y ) uf[x] = y;
}

bool  LT(double a,double b) { return !equals(a,b) && a < b; }
bool LTE(double a,double b) { return  equals(a,b) || a < b; }

// true -> CCW, false -> CW
bool rotationCheck(vector<Point>& vec){
  double sum = 0;
  for(int i=0;i<vec.size();i++) sum += cross(vec[i],vec[(i+1)%vec.size()]);
  return ( !equals(sum,0.0) && sum > 0 ); 
}


Point vir;
bool comp_angle(const Edge &a, const Edge &b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}


bool inPolygon(Polygon a,Polygon b) {
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
    if( !inPolygon(b,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) rep(j,(int)b.size()) {
    Segment seg_a = Segment(a[i],a[(i+1)%(int)a.size()]);
    Segment seg_b = Segment(b[j],b[(j+1)%(int)b.size()]);
    if( equals(cross(seg_a.p1-seg_a.p2,seg_b.p1-seg_b.p2),0.0) ) {
      if( onSegment(seg_a.p1,seg_a.p2,seg_b.p1) || onSegment(seg_a.p1,seg_a.p2,seg_b.p2) ||
          onSegment(seg_b.p1,seg_b.p2,seg_a.p1) || onSegment(seg_b.p1,seg_b.p2,seg_a.p2) ) {
        vector<Point> tmp;
        tmp.push_back(seg_a.p1), tmp.push_back(seg_a.p2);
        tmp.push_back(seg_b.p1), tmp.push_back(seg_b.p2);
        sort(tmp.begin(),tmp.end());
        if( tmp[1] == tmp[2] ) continue;
        return true;
      }
    }
  }
  rep(i,(int)a.size()) {
    Point mp = ( a[i] + a[(i+1)%(int)a.size()] ) / 2.0;
      if( inPolygon(b,mp) ) return true;
  }
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
      if( inPolygon(a,mp) ) return true;
  }
  return false;
}

int N;

void compute(vector<Segment> &segs){
  G = segmentArrangement(segs,vp);
  int V = G.size();

  assert( V < MAX_V );

  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }

  rep(i,V) rep(j,(int)G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,(int)G[to].size()) if( G[to][k].to == i ) {
      G[i][j].rev  = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> polies;
  rep(i,V) rep(j,(int)G[i].size()) if( !visited[i][j] ) {
    vector<Point> buf;
    set<Point> used;
    if( getPath(i,i,j,buf,used) ) if( (int)buf.size() >= 3 ) {
        polies.push_back(buf);
      }
  }
  V = polies.size();
  /*
  cout << V << endl;
  rep(i,V){
    cout << i << "-th:" << endl;
    rep(j,polies[i].size()) cout << polies[i][j] << " "; cout << endl;
  }
  */
  init(V);
  set<int> S;
  rep(i,V) rep(j,V) if( i != j ) {
    if( canUnit(polies[i],polies[j]) ) unit(i,j);
  }
  rep(i,V) S.insert(find(i));
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  vector<Segment> segs(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute(segs);
  return 0;
}
accuracy: 0.7
time_ms: 350
memory_kb: 2956
final_rank: 12

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=0,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size()) REP(j,i+1,vs.size()) {
    if(intersectSS(vs[i],vs[j])) {
      ps.push_back(Point(crosspoint(vs[i],vs[j])));
    }
  }

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}





///////////// にゃんこまっしぐら ////////////////////////
vector<vector<Edge> > G;
vector<Point> vp;
const int MAX_V = 8000;
bool visited[MAX_V][MAX_V];

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &S){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
 
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  if( S.count(vp[G[next_node][next_edge].from]) ) return visited[node][edge] = false;
  S.insert(vp[G[next_node][next_edge].from]);
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,S)) return true;
  else return visited[node][edge] = false;
}
///////////// にゃんにゃん ////////////////////////////////

bool inPolygon(Polygon a,Polygon b){
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
    if( !inPolygon(a,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) {
    rep(j,(int)b.size()){
      if( ( onSegment(a[i],a[(i+1)%(int)a.size()],b[j]) && onSegment(a[i],a[(i+1)%(int)a.size()],b[(j+1)%(int)b.size()]) ) ||
          ( onSegment(b[j],b[(j+1)%(int)b.size()],a[i]) && onSegment(b[j],b[(j+1)%(int)b.size()],a[(i+1)%(int)a.size()]) ) ) return true;
      if( intersectSS(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()])) ) {
        Point cp = crosspoint(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()]));
        if( cp == a[i] || cp == a[(i+1)%(int)a.size()] || cp == b[j] || cp == b[(j+1)%(int)b.size()] ) continue;
        return true;
      }
    }
  }
  return false;
}

int N;
vector<Segment> segs;

bool LT(double a,double b) { return !equals(a,b) && a < b; }

Point vir;
bool comp_angle(Edge a,Edge b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

int uf[MAX_V];

int find(int x){
  if(x == uf[x])return x;
  return uf[x]=find(uf[x]);
}
 
void unit(int x,int y){
  x = find(x), y = find(y);
  if(x!=y) uf[x] = y;
}

void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }


void compute(){
  G = segmentArrangement(segs,vp);

  int V = vp.size();  assert( V < MAX_V );
  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }
  rep(i,V) rep(j,G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,G[to].size())if( G[to][k].to == i ){
      G[i][j].rev = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> poly;
  rep(i,V) rep(j,G[i].size()) if( !visited[i][j]){
    Polygon buf;
    set<Point> S;
    if( getPath(i,i,j,buf,S) ) {
      if( buf.size() >= 3 )
      poly.push_back(buf);
    }
  }
  /*
  cout << poly.size() << endl;
  rep(i,poly.size()){
    cout << i << "-th" << endl;
    rep(j,poly[i].size()) cout << poly[i][j] << " "; cout << endl;
  }
  */
  V = poly.size();
  assert( V < MAX_V );
  init(V);
  rep(i,V) rep(j,V) if( i != j && canUnit(poly[i],poly[j]) ) unit(i,j);
  set<int> S;
  rep(i,V) S.insert(find(i));
  //rep(i,V) cout << i << "-th : " << find(i) << endl;
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  segs.resize(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute();
  return 0;
}
accuracy: 0.7
time_ms: 150
memory_kb: 4564
final_rank: 15

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-8)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=0,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size()) REP(j,i+1,vs.size()) {
    if(intersectSS(vs[i],vs[j])) {
      ps.push_back(Point(crosspoint(vs[i],vs[j])));
    }
  }

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}





///////////// にゃんこまっしぐら ////////////////////////
vector<vector<Edge> > G;
vector<Point> vp;
const int MAX_V = 8000;
bool visited[MAX_V][MAX_V];

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &S){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
 
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  if( S.count(vp[G[next_node][next_edge].from]) ) return visited[node][edge] = false;
  S.insert(vp[G[next_node][next_edge].from]);
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,S)) return true;
  else return visited[node][edge] = false;
}
///////////// にゃんにゃん ////////////////////////////////

bool inPolygon(Polygon a,Polygon b){
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
    if( !inPolygon(a,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) {
    rep(j,(int)b.size()){
      if( ( onSegment(a[i],a[(i+1)%(int)a.size()],b[j]) && onSegment(a[i],a[(i+1)%(int)a.size()],b[(j+1)%(int)b.size()]) ) ||
          ( onSegment(b[j],b[(j+1)%(int)b.size()],a[i]) && onSegment(b[j],b[(j+1)%(int)b.size()],a[(i+1)%(int)a.size()]) ) ) return true;
      if( intersectSS(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()])) ) {
        Point cp = crosspoint(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()]));
        if( cp == a[i] || cp == a[(i+1)%(int)a.size()] || cp == b[j] || cp == b[(j+1)%(int)b.size()] ) continue;
        return true;
      }
    }
  }
  return false;
}

int N;
vector<Segment> segs;

bool LT(double a,double b) { return !equals(a,b) && a < b; }

Point vir;
bool comp_angle(Edge a,Edge b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

int uf[MAX_V];

int find(int x){
  if(x == uf[x])return x;
  return uf[x]=find(uf[x]);
}
 
void unit(int x,int y){
  x = find(x), y = find(y);
  if(x!=y) uf[x] = y;
}

void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }


void compute(){
  G = segmentArrangement(segs,vp);

  int V = vp.size();  assert( V < MAX_V );
  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }
  rep(i,V) rep(j,G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,G[to].size())if( G[to][k].to == i ){
      G[i][j].rev = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> poly;
  rep(i,V) rep(j,G[i].size()) {
    Polygon buf;
    set<Point> S;
    if( getPath(i,i,j,buf,S) ) {
      if( buf.size() >= 3 )
      poly.push_back(buf);
    }
  }
  /*
  cout << endl;
  rep(i,poly.size()){
    cout << i << "-th" << endl;
    rep(j,poly[i].size()) cout << poly[i][j] << " "; cout << endl;
  }
  */
  V = poly.size();
  assert( V < MAX_V );
  init(V);
  rep(i,V) rep(j,V) if( i != j && canUnit(poly[i],poly[j]) ) unit(i,j);
  set<int> S;
  rep(i,V) S.insert(find(i));
  //rep(i,V) cout << i << "-th : " << find(i) << endl;
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  segs.resize(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute();
  return 0;
}
accuracy: 0.7
time_ms: 150
memory_kb: 4564
final_rank: 15

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=0,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size()) REP(j,i+1,vs.size()) {
    if(intersectSS(vs[i],vs[j])) {
      ps.push_back(Point(crosspoint(vs[i],vs[j])));
    }
  }

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}





///////////// にゃんこまっしぐら ////////////////////////
vector<vector<Edge> > G;
vector<Point> vp;
const int MAX_V = 8000;
bool visited[MAX_V][MAX_V];

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &S){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
 
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  if( S.count(vp[G[next_node][next_edge].from]) ) return visited[node][edge] = false;
  S.insert(vp[G[next_node][next_edge].from]);
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,S)) return true;
  else return visited[node][edge] = false;
}
///////////// にゃんにゃん ////////////////////////////////

bool inPolygon(Polygon a,Polygon b){
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
    if( !inPolygon(a,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) {
    rep(j,(int)b.size()){
      if( ( onSegment(a[i],a[(i+1)%(int)a.size()],b[j]) && onSegment(a[i],a[(i+1)%(int)a.size()],b[(j+1)%(int)b.size()]) ) ||
          ( onSegment(b[j],b[(j+1)%(int)b.size()],a[i]) && onSegment(b[j],b[(j+1)%(int)b.size()],a[(i+1)%(int)a.size()]) ) ) return true;
      if( intersectSS(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()])) ) {
        Point cp = crosspoint(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()]));
        if( cp == a[i] || cp == a[(i+1)%(int)a.size()] || cp == b[j] || cp == b[(j+1)%(int)b.size()] ) continue;
        return true;
      }
    }
  }
  return false;
}

int N;
vector<Segment> segs;

bool LT(double a,double b) { return !equals(a,b) && a < b; }

Point vir;
bool comp_angle(Edge a,Edge b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

int uf[MAX_V];

int find(int x){
  if(x == uf[x])return x;
  return uf[x]=find(uf[x]);
}
 
void unit(int x,int y){
  x = find(x), y = find(y);
  if(x!=y) uf[x] = y;
}

void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }


void compute(){
  G = segmentArrangement(segs,vp);

  int V = vp.size();  assert( V < MAX_V );
  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }
  rep(i,V) rep(j,G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,G[to].size())if( G[to][k].to == i ){
      G[i][j].rev = k;
      G[to][k].rev = j;
      break;
    }
  }

  vector<Polygon> poly;
  rep(i,V) rep(j,G[i].size()) {
    Polygon buf;
    set<Point> S;
    if( getPath(i,i,j,buf,S) ) {
      if( buf.size() >= 3 )
      poly.push_back(buf);
    }
  }
  /*
  cout << endl;
  rep(i,poly.size()){
    cout << i << "-th" << endl;
    rep(j,poly[i].size()) cout << poly[i][j] << " "; cout << endl;
  }
  */
  V = poly.size();
  assert( V < MAX_V );
  init(V);
  rep(i,V) rep(j,V) if( i != j && canUnit(poly[i],poly[j]) ) unit(i,j);
  set<int> S;
  rep(i,V) S.insert(find(i));
  //rep(i,V) cout << i << "-th : " << find(i) << endl;
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  segs.resize(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute();
  return 0;
}
accuracy: 0.7
time_ms: 150
memory_kb: 4564
final_rank: 15

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=0,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size()) REP(j,i+1,vs.size()) {
    if(intersectSS(vs[i],vs[j])) {
      ps.push_back(Point(crosspoint(vs[i],vs[j])));
    }
  }

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}





///////////// にゃんこまっしぐら ////////////////////////
vector<vector<Edge> > G;
vector<Point> vp;
const int MAX_V = 8000;
bool visited[MAX_V][MAX_V];

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &S){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
 
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  if( S.count(vp[G[node][edge].from]) ) return visited[node][edge] = false;
  S.insert(vp[G[node][edge].from]);
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,S)) return true;
  else return visited[node][edge] = false;
}
///////////// にゃんにゃん ////////////////////////////////

bool inPolygon(Polygon a,Polygon b){
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
    if( !inPolygon(a,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) {
    rep(j,(int)b.size()){
      if( ( onSegment(a[i],a[(i+1)%(int)a.size()],b[j]) && onSegment(a[i],a[(i+1)%(int)a.size()],b[(j+1)%(int)b.size()]) ) ||
          ( onSegment(b[j],b[(j+1)%(int)b.size()],a[i]) && onSegment(b[j],b[(j+1)%(int)b.size()],a[(i+1)%(int)a.size()]) ) ) return true;
      if( intersectSS(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()])) ) {
        Point cp = crosspoint(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()]));
        if( cp == a[i] || cp == a[(i+1)%(int)a.size()] || cp == b[j] || cp == b[(j+1)%(int)b.size()] ) continue;
        return true;
      }
    }
  }
  return false;
}

int N;
vector<Segment> segs;

bool LT(double a,double b) { return !equals(a,b) && a < b; }

Point vir;
bool comp_angle(Edge a,Edge b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

int uf[MAX_V];

int find(int x){
  if(x == uf[x])return x;
  return uf[x]=find(uf[x]);
}
 
void unit(int x,int y){
  x = find(x), y = find(y);
  if(x!=y) uf[x] = y;
}

void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }


void compute(){
  G = segmentArrangement(segs,vp);

  int V = vp.size();  assert( V < MAX_V );
  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }
  rep(i,V) rep(j,G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,G[to].size())if( G[to][k].to == i ){
      G[i][j].rev = k;
      G[to][k].rev = j;
      break;
    }
  }
  
  vector<Polygon> poly;
  rep(i,V) rep(j,G[i].size()) {
    Polygon buf;
    set<Point> S;
    if( getPath(i,i,j,buf,S) ) {
      if( buf.size() >= 3)
      poly.push_back(buf);
    }
  }
  /*
  cout << endl;
  rep(i,poly.size()){
    cout << i << "-th" << endl;
    rep(j,poly[i].size()) cout << poly[i][j] << " "; cout << endl;
  }
  */
  V = poly.size();
  assert( V < MAX_V );
  init(V);
  rep(i,V) rep(j,V) if( i != j && canUnit(poly[i],poly[j]) ) unit(i,j);
  set<int> S;
  rep(i,V) S.insert(find(i));
  //rep(i,V) cout << i << "-th : " << find(i) << endl;
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  segs.resize(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute();
  return 0;
}
accuracy: 0.7
time_ms: 150
memory_kb: 4564
final_rank: 15

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

#define REP(i,s,n) for(int i=s;i<n;i++)
#define rep(i,n) REP(i,0,n)
#define EPS (1e-6)
#define equals(a,b) (fabs((a)-(b)) < EPS)
#define COUNTER_CLOCKWISE 1
#define CLOCKWISE -1 
#define ONLINE_BACK 2
#define ONLINE_FRONT -2
#define ON_SEGMENT 0

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);}
  Point operator * (Point a){ return Point(x * a.x - y * a.y, x * a.y + y * a.x); }

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

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

};

struct Segment{
  Point p1,p2;
  Segment(Point p1 = Point(),Point p2 = Point()):p1(p1),p2(p2){}
  bool operator <  (const Segment& s) const { return ( p2 == s.p2 ) ? p1 < s.p1 : p2 < s.p2; }
  bool operator == (const Segment& s) const { return ( s.p1 == p1 && s.p2 == p2 ) || ( s.p1 == p2 && s.p2 == p1 ); }

};

typedef Point Vector;
typedef Segment Line;
typedef vector<Point> Polygon;

ostream& operator << (ostream& os,const Point& a){ os << "(" << a.x << "," << a.y << ")"; }

ostream& operator << (ostream& os,const Segment& a){ os << "( " << a.p1 << " , " << a.p2 << " )"; }

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

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

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

int ccw(Point p0,Point p1,Point p2){
  Point a = p1-p0;
  Point 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 intersectLL(Line l, Line m) {
  return abs(cross(l.p2-l.p1, m.p2-m.p1)) > EPS || // non-parallel
         abs(cross(l.p2-l.p1, m.p1-l.p1)) < EPS;   // same line
}
bool intersectLS(Line l, Line s) {
  return cross(l.p2-l.p1, s.p1-l.p1)*       // s[0] is left of l
         cross(l.p2-l.p1, s.p2-l.p1) < EPS; // s[1] is right of l
}
bool intersectLP(Line l,Point p) {
  return abs(cross(l.p2-p, l.p1-p)) < EPS;
}
bool intersectSS(Line s, Line t) {
  return ccw(s.p1,s.p2,t.p1)*ccw(s.p1,s.p2,t.p2) <= 0 &&
         ccw(t.p1,t.p2,s.p1)*ccw(t.p1,t.p2,s.p2) <= 0;
}
bool intersectSP(Line s, Point p) {
  return abs(s.p1-p)+abs(s.p2-p)-abs(s.p2-s.p1) < EPS; // triangle inequality
}

Point projection(Line l,Point p) {
  double t = dot(p-l.p1, l.p1-l.p2) / norm(l.p1-l.p2);
  return l.p1 + (l.p1-l.p2)*t;
}
Point reflection(Line l,Point p) {
  return p + (projection(l, p) - p) * 2;
}
double distanceLP(Line l, Point p) {
  return abs(p - projection(l, p));
}
double distanceLL(Line l, Line m) {
  return intersectLL(l, m) ? 0 : distanceLP(l, m.p1);
}

double distanceLS(Line l, Line s) {
  if (intersectLS(l, s)) return 0;
  return min(distanceLP(l, s.p1), distanceLP(l, s.p2));
}
double distanceSP(Line s, Point p) {
  Point r = projection(s, p);
  if (intersectSP(s, r)) return abs(r - p);
  return min(abs(s.p1 - p), abs(s.p2 - p));
}

double distanceSS(Line s, Line t) {
  if (intersectSS(s, t)) return 0;
  return min(min(distanceSP(s, t.p1), distanceSP(s, t.p2)),
             min(distanceSP(t, s.p1), distanceSP(t, s.p2)));
}

Point crosspoint(Line l,Line m){
  double A = cross(l.p2-l.p1,m.p2-m.p1);
  double B = cross(l.p2-l.p1,l.p2-m.p1);
  if(abs(A) < EPS && abs(B) < EPS){
    vector<Point> vec;
    vec.push_back(l.p1),vec.push_back(l.p2),vec.push_back(m.p1),vec.push_back(m.p2);
    sort(vec.begin(),vec.end());
    assert(vec[1] == vec[2]); //同じセグメントかもよ
    return vec[1];
    //return m.p1;
  }
  if(abs(A) < EPS)assert(false);
  return m.p1 + (m.p2-m.p1)*(B/A);
}

//cross product of pq and pr
double cross3p(Point p,Point q,Point r) { return (r.x-q.x) * (p.y -q.y) - (r.y - q.y) * (p.x - q.x); }
  
//returns true if point r is on the same line as the line pq
bool collinear(Point p,Point q,Point r) { return fabs(cross3p(p,q,r)) < EPS; }
  
//returns true if point t is on the left side of line pq
bool ccwtest(Point p,Point q,Point r){
  return cross3p(p,q,r) > 0; //can be modified to accept collinear points
}
 
bool onSegment(Point p,Point q,Point r){
  return collinear(p,q,r) && equals(sqrt(pow(p.x-r.x,2)+pow(p.y-r.y,2)) + sqrt(pow(r.x-q.x,2) + pow(r.y-q.y,2) ),sqrt(pow(p.x-q.x,2)+pow(p.y-q.y,2)) ) ;
}
  

bool isConvex(vector<Point> p) {
  int sz = (int)p.size();
  if(sz < 3)return false;//boundary case, we treat a point or a line as not convex
  bool isLeft = ccwtest(p[0],p[1],p[2]);
  for(int i=1; i<(int)p.size();i++)
    if(ccwtest(p[i],p[(i+1)%sz],p[(i+2)%sz]) != isLeft) return false;
  return true;
}


double angle(Point a,Point b,Point c) {
  double ux = b.x - a.x, uy = b.y - a.y;
  double vx = c.x - a.x, vy = c.y - a.y;
  return acos((ux*vx + uy*vy)/sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy)));
}  
  
//多角形poly内（線分上も含む）に点pが存在するかどうは判定する  
bool inPolygon(Polygon poly,Point p){
  if((int)poly.size() == 0)return false;
  rep(i,poly.size()) if(onSegment(poly[i],poly[(i+1)%poly.size()],p))return true;
  double sum = 0;
  for(int i=0; i < (int)poly.size() ;i++) {
    if( equals(cross(poly[i]-p,poly[(i+1)%poly.size()]-p),0.0) ) continue; // ３点が平行だとangleがnanを返しsumがnanになり死ぬ
    if( cross3p(p,poly[i],poly[(i+1)%poly.size()]) < 0 ) sum -= angle(p,poly[i],poly[(i+1)%poly.size()]);
    else                                                 sum += angle(p,poly[i],poly[(i+1)%poly.size()]);
  }
  // あまり誤差を厳しくしすぎると良くないので以下のほうが良い 
  const double eps = 1e-5;
  return (fabs(sum - 2*M_PI) < eps || fabs(sum + 2*M_PI) < eps);
}  

struct Edge {
  int from,to,rev;
  double cost;
  Edge(int from=0,int to=0,int rev=0,double cost=0):from(from),to(to),rev(rev),cost(cost){}
  bool operator < (const Edge& a)const { return !equals(cost,a.cost) && cost < a.cost; }
};

vector<vector<Edge> > segmentArrangement(vector<Segment> vs,vector<Point> &ps) {

  rep(i,vs.size()) REP(j,i+1,vs.size()) {
    if(intersectSS(vs[i],vs[j])) {
      ps.push_back(Point(crosspoint(vs[i],vs[j])));
    }
  }

  sort(ps.begin(),ps.end());  
  ps.erase(unique(ps.begin(),ps.end()),ps.end());

  vector<vector<Edge> > ret(ps.size());

  for(int i=0;i<vs.size();i++){
    vector<pair<double,int> > list;
    rep(j,ps.size()) if(intersectSP(vs[i],ps[j]))list.push_back(pair<double,int>(norm(vs[i].p1-ps[j]),j));
    sort(list.begin(),list.end());
    for(int j=0;j+1<list.size();++j) {
      int from = list[j].second, to = list[j+1].second;
      double cost = abs(ps[from]-ps[to]);
      ret[from].push_back(Edge(from,to,-1,cost));
      ret[to].push_back(Edge(to,from,-1,cost));
    }
  }  
  return ret;
}





///////////// にゃんこまっしぐら ////////////////////////
vector<vector<Edge> > G;
vector<Point> vp;
const int MAX_V = 8000;
bool visited[MAX_V][MAX_V];

bool getPath(int start_node,int node,int edge,vector<Point> &buf,set<Point> &S){
  if( visited[node][edge] ){
    if( !buf.empty() && node == start_node ) return true;
    return false;
  }
  visited[node][edge] = true;
 
  int next_node = G[node][edge].to;
  int next_edge = (G[node][edge].rev+1)%((int)G[next_node].size());
  if( S.count(vp[G[node][edge].from]) ) return false;
  S.insert(vp[G[node][edge].from]);
  buf.push_back(vp[G[node][edge].from]);
  if(getPath(start_node,next_node,next_edge,buf,S)) return true;
  else return visited[node][edge] = false;
}
///////////// にゃんにゃん ////////////////////////////////

bool inPolygon(Polygon a,Polygon b){
  rep(i,(int)b.size()) {
    Point mp = ( b[i] + b[(i+1)%(int)b.size()] ) / 2.0;
    if( !inPolygon(a,mp) ) return false;
  }
  return true;
}

bool canUnit(Polygon a,Polygon b){
  if( inPolygon(a,b) || inPolygon(b,a) ) return true;
  rep(i,(int)a.size()) {
    rep(j,(int)b.size()){
      if( ( onSegment(a[i],a[(i+1)%(int)a.size()],b[j]) && onSegment(a[i],a[(i+1)%(int)a.size()],b[(j+1)%(int)b.size()]) ) ||
          ( onSegment(b[j],b[(j+1)%(int)b.size()],a[i]) && onSegment(b[j],b[(j+1)%(int)b.size()],a[(i+1)%(int)a.size()]) ) ) return true;
      if( intersectSS(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()])) ) {
        Point cp = crosspoint(Segment(a[i],a[(i+1)%(int)a.size()]),Segment(b[j],b[(j+1)%(int)b.size()]));
        if( cp == a[i] || cp == a[(i+1)%(int)a.size()] || cp == b[j] || cp == b[(j+1)%(int)b.size()] ) continue;
        return true;
      }
    }
  }
  return false;
}

int N;
vector<Segment> segs;

bool LT(double a,double b) { return !equals(a,b) && a < b; }

Point vir;
bool comp_angle(Edge a,Edge b){
  Vector va = vir - vp[a.to];
  Vector vb = vir - vp[b.to];
  return LT(atan2(va.y,va.x),atan2(vb.y,vb.x));
}

int uf[MAX_V];

int find(int x){
  if(x == uf[x])return x;
  return uf[x]=find(uf[x]);
}
 
void unit(int x,int y){
  x = find(x), y = find(y);
  if(x!=y) uf[x] = y;
}

void init(int _size=MAX_V) { rep(i,_size) uf[i] = i; }


void compute(){
  G = segmentArrangement(segs,vp);

  int V = vp.size();  assert( V < MAX_V );
  rep(i,V) { vir = vp[i]; sort(G[i].begin(),G[i].end(),comp_angle); }
  rep(i,V) rep(j,G[i].size()) if( G[i][j].rev == -1 ) {
    int to = G[i][j].to;
    rep(k,G[to].size())if( G[to][k].to == i ){
      G[i][j].rev = k;
      G[to][k].rev = j;
      break;
    }
  }
  
  vector<Polygon> poly;
  rep(i,V) rep(j,G[i].size()) {
    Polygon buf;
    set<Point> S;
    if( getPath(i,i,j,buf,S) ) {
      if( buf.size() >= 3)
      poly.push_back(buf);
    }
  }
  /*
  cout << endl;
  rep(i,poly.size()){
    cout << i << "-th" << endl;
    rep(j,poly[i].size()) cout << poly[i][j] << " "; cout << endl;
  }
  */
  V = poly.size();
  assert( V < MAX_V );
  init(V);
  rep(i,V) rep(j,V) if( i != j && canUnit(poly[i],poly[j]) ) unit(i,j);
  set<int> S;
  rep(i,V) S.insert(find(i));
  //rep(i,V) cout << i << "-th : " << find(i) << endl;
  cout << (int)S.size() << endl;
}

int main(){
  cin >> N;
  segs.resize(N);
  rep(i,N)  cin >> segs[i].p1.x >> segs[i].p1.y >> segs[i].p2.x >> segs[i].p2.y;
  compute();
  return 0;
}
accuracy: 0.7
time_ms: 150
memory_kb: 4568
final_rank: 19

code_snippet: #include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

#define REP(i, a, b) for(int i = (a); i < int(b); ++i)
#define rep(i, n) REP(i, 0, n)
#define ALL(x) begin(x), end(x)

constexpr double EPS = 1e-8;

struct point {
	double x, y;
	explicit point(double x_ = 0, double y_ = 0):x(x_), y(y_) {}
	point(const point &p):x(p.x), y(p.y) {}

	inline point operator+(const point &p) const {
		return point(x + p.x, y + p.y);
	}

	inline point operator-(const point &p) const {
		return point(x - p.x, y - p.y);
	}

	inline point operator*(double s) const {
		return point(x * s, y * s);
	}

	inline bool operator<(const point &p) const {
		return x + EPS < p.x || (abs(x - p.x) < EPS && y + EPS < p.y);
	}

	inline bool operator==(const point &p) const {
		return abs(x - p.x) < EPS && abs(y - p.y) < EPS;
	}
};

typedef vector<point> polygon;

struct segment {
	point a, b;
	segment(const point &a_, const point &b_):a(a_), b(b_) {}
};

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

inline double norm(const point &p) {
	return p.x * p.x + p.y * p.y;
}

inline double abs(const point &p) {
	return sqrt(norm(p));
}

inline double angle(const point &p) {
	return atan2(p.y, p.x);
}

inline int ccw(const point &a, point b, point c) {
	b = b - a;
	c = c - a;
	const double tmp = cross(b, c);
	if(tmp > EPS) return 1; // ccw
	if(tmp < -EPS) return -1; // cw
	if(dot(b, c) < 0) return 2; // c, a, b 順に一直線上
	if(norm(b) < norm(c)) return -2; // a, b, c 順に一直線上
	return 0; //a, c, b 順で一直線上
}

inline bool intersect(const segment &s, const segment &t) {
	const int c1 = ccw(s.a, s.b, t.a);
	const int c2 = ccw(s.a, s.b, t.b);
	const int c3 = ccw(t.a, t.b, s.a);
	const int c4 = ccw(t.a, t.b, s.b);
	return (c1 * c2 < 0 || (c1 == 0 && c1 == c2)) && (c3 * c4 < 0 || (c3 == 0 && c3 == c4));
}

inline bool intersect(const segment &s, const point &p) {
	return ccw(s.a, s.b, p) == 0;
}

inline point crosspoint(const segment &s, const segment &t) {
	const double tmp = cross(s.b - s.a, t.b - t.a);
	if(abs(tmp) < EPS) { // 一直線上
		if(intersect(s, t.a)) return t.a;
		if(intersect(s, t.b)) return t.b;
		if(intersect(t, s.a)) return s.a;
		return s.b;
	}
	return t.a + (t.b - t.a) * cross(s.b - s.a, s.b - t.a) * (1.0 / tmp);
}

inline int sign(double d) {
	return d > EPS ? 1 : d < -EPS ? -1 : 0;
}

inline int judge(const point &a, const point &b, const point &c) {
	return sign(cross(a, c)) - sign(cross(b, c)) - sign(cross(a, b));
}

inline bool contain(const polygon &P, const point &p) {
	bool res = false;

	point prev = P.back();
	for(const auto &curr : P) {
		point v1 = prev - p, v2 = curr - p;
		if(v1.y > v2.y) swap(v1, v2);
		if(v1.y < EPS && EPS < v2.y && cross(v1, v2) > EPS) res = !res;
		if(abs(cross(v1, v2)) < EPS && dot(v1, v2) < EPS) return false;
		prev = curr;
	}
	return res;
}

inline bool intersect(const polygon &P, const segment &s) {
	if(contain(P, s.a) || contain(P, s.b)) return true;

	point prev = P.back();
	for(const auto &curr : P) {
		if(intersect(s, segment(prev, curr))) return true;
		prev = curr;
	}

	return false;
}

inline bool intersect(const polygon &A, const polygon &B) {
	point prev = B.back();

	for(const auto &curr : B) {
		if(intersect(A, segment(prev, curr))) return true;
		prev = curr;
	}

	for(const auto &p : A) {
		if(contain(B, p)) return true;
	}
	
	return false;
}

struct edge {
	int to;
	double theta;
	bool used;
	edge(int to_, double theta_):to(to_), theta(theta_), used(false) {}

	inline bool operator<(const edge &e) const {
		return theta < e.theta;
	}
};

vector<vector<edge>> arrangement(const vector<segment> &segments, vector<point> &points) {
	const int n = segments.size();
	points.clear();

	for(int i = 0; i < n; ++i) {
		const auto &s1 = segments[i];
		points.emplace_back(s1.a);
		points.emplace_back(s1.b);

		for(int j = i + 1; j < n; ++j) {
			const auto &s2 = segments[j];
			if(intersect(s1, s2)) {
				points.emplace_back(crosspoint(s1, s2));
			}
		}
	}

	sort(points.begin(), points.end());
	points.erase(unique(points.begin(), points.end()), points.end());

	const int V = points.size();
	vector<vector<edge>> G(V);

	for(const auto &s : segments) {
		vector<pair<double, int>> vs;
		for(int i = 0; i < V; ++i) {
			if(intersect(s, points[i])) {
				vs.emplace_back(abs(s.a - points[i]), i);
			}
		}

		sort(vs.begin(), vs.end());
		for(int i = 1; i < static_cast<int>(vs.size()); ++i) {
			const int v = vs[i].second;
			const int u = vs[i - 1].second;
			G[v].emplace_back(u, angle(points[u] - points[v]));
			G[u].emplace_back(v, angle(points[v] - points[u]));
		}
	}

	for(auto &es : G) sort(es.begin(), es.end());
	return G;
}

vector<polygon> find_faces(const vector<point> &points, vector<vector<edge>> &G) {
	const int n = G.size();
	vector<polygon> res;

	rep(i, n) {
		rep(j, G[i].size()) {
			int v = i;
			int e_idx = j;
			polygon P;

			while(true) {
				edge &e = G[v][e_idx];
				if(e.used) break;

				e.used = true;
				P.emplace_back(points[v]);

				rep(k, G[e.to].size()) {
					if(G[e.to][k].to == v) {
						e_idx = (k + 1) % G[e.to].size();
						v = e.to;
						break;
					}
				}
			}

			if(P.size() >= 3) res.emplace_back(move(P));
		}
	}

	unsigned idx = 0;
	while(idx < res.size()) {
		const point diff = res[idx][1] - res[idx][0];
		const point p = res[idx][0] + diff * 0.5 + point(diff.y, -diff.x) * (2.0 * EPS / abs(diff));
		if(contain(res[idx], p)) {
			++idx;
		}
		else {
			res.erase(res.begin() + idx);
		}
	}

	return res;
}

int main() {
	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	int n;
	cin >> n;

	vector<segment> segments;
	segments.reserve(n);

	rep(i, n) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		segments.emplace_back(point(x1, y1), point(x2, y2));
	}

	vector<point> points;
	auto G = arrangement(segments, points);
	const auto faces = find_faces(points, G);

	const int num = faces.size();
	vector<vector<bool>> mat(num, vector<bool>(num, false));

	rep(i, num) {
		rep(j, i) {
			if(intersect(faces[i], faces[j])) {
				mat[i][j] = mat[j][i] = true;
			}
		}
	}

	int ans = 0;
	vector<bool> used(num, false);
	rep(i, num) {
		if(used[i]) continue;

		++ans;
		used[i] = true;
		queue<int> que;
		que.push(i);

		while(!que.empty()) {
			const int v = que.front();
			que.pop();

			rep(j, num) {
				if(!used[j] && mat[v][j]) {
					que.push(j);
					used[j] = true;
				}
			}
		}
	}
	
	cout << ans << endl;

	return EXIT_SUCCESS;
}
accuracy: 1
time_ms: 20
memory_kb: 1448
final_rank: 1

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

#define REP(i, a, b) for(int i = (a); i < int(b); ++i)
#define rep(i, n) REP(i, 0, n)
#define ALL(x) begin(x), end(x)

constexpr double EPS = 1e-8;

struct point {
	double x, y;
	explicit point(double x_ = 0, double y_ = 0):x(x_), y(y_) {}
	point(const point &p):x(p.x), y(p.y) {}

	inline point operator+(const point &p) const {
		return point(x + p.x, y + p.y);
	}

	inline point operator-(const point &p) const {
		return point(x - p.x, y - p.y);
	}

	inline point operator*(double s) const {
		return point(x * s, y * s);
	}

	inline bool operator<(const point &p) const {
		return x + EPS < p.x || (abs(x - p.x) < EPS && y + EPS < p.y);
	}

	inline bool operator==(const point &p) const {
		return abs(x - p.x) < EPS && abs(y - p.y) < EPS;
	}
};

typedef vector<point> polygon;

struct segment {
	point a, b;
	segment(const point &a_, const point &b_):a(a_), b(b_) {}
};

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

inline double norm(const point &p) {
	return p.x * p.x + p.y * p.y;
}

inline double abs(const point &p) {
	return sqrt(norm(p));
}

inline double angle(const point &p) {
	return atan2(p.y, p.x);
}

inline int ccw(const point &a, point b, point c) {
	b = b - a;
	c = c - a;
	const double tmp = cross(b, c);
	if(tmp > EPS) return 1; // ccw
	if(tmp < -EPS) return -1; // cw
	if(dot(b, c) < 0) return 2; // c, a, b 順に一直線上
	if(norm(b) < norm(c)) return -2; // a, b, c 順に一直線上
	return 0; //a, c, b 順で一直線上
}

inline bool intersect(const segment &s, const segment &t) {
	const int c1 = ccw(s.a, s.b, t.a);
	const int c2 = ccw(s.a, s.b, t.b);
	const int c3 = ccw(t.a, t.b, s.a);
	const int c4 = ccw(t.a, t.b, s.b);
	return (c1 * c2 < 0 || (c1 == 0 && c1 == c2)) && (c3 * c4 < 0 || (c3 == 0 && c3 == c4));
}

inline bool intersect(const segment &s, const point &p) {
	return ccw(s.a, s.b, p) == 0;
}

inline point crosspoint(const segment &s, const segment &t) {
	const double tmp = cross(s.b - s.a, t.b - t.a);
	if(abs(tmp) < EPS) { // 一直線上
		if(intersect(s, t.a)) return t.a;
		if(intersect(s, t.b)) return t.b;
		if(intersect(t, s.a)) return s.a;
		return s.b;
	}
	return t.a + (t.b - t.a) * cross(s.b - s.a, s.b - t.a) * (1.0 / tmp);
}

inline int sign(double d) {
	return d > EPS ? 1 : d < -EPS ? -1 : 0;
}

inline int judge(const point &a, const point &b, const point &c) {
	return sign(cross(a, c)) - sign(cross(b, c)) - sign(cross(a, b));
}

inline bool contain(const polygon &P, const point &p) {
	bool res = false;

	point prev = P.back();
	for(const auto &curr : P) {
		point v1 = prev - p, v2 = curr - p;
		if(v1.y > v2.y) swap(v1, v2);
		if(v1.y < EPS && EPS < v2.y && cross(v1, v2) > EPS) res = !res;
		if(abs(cross(v1, v2)) < EPS && dot(v1, v2) < EPS) return false;
		prev = curr;
	}
	return res;
}

inline bool intersect(const polygon &P, const segment &s) {
	if(contain(P, s.a) || contain(P, s.b)) return true;

	point prev = P.back();
	for(const auto &curr : P) {
		if(intersect(s, segment(prev, curr))) return true;
		prev = curr;
	}

	return false;
}

inline bool intersect(const polygon &A, const polygon &B) {
	point prev = B.back();

	for(const auto &curr : B) {
		if(intersect(A, segment(prev, curr))) return true;
		prev = curr;
	}

	for(const auto &p : A) {
		if(contain(B, p)) return true;
	}
	
	return false;
}

struct edge {
	int to;
	double theta;
	bool used;
	edge(int to_, double theta_):to(to_), theta(theta_), used(false) {}

	inline bool operator<(const edge &e) const {
		return theta < e.theta;
	}
};

vector<vector<edge>> arrangement(const vector<segment> &segments, vector<point> &points) {
	const int n = segments.size();
	points.clear();

	for(int i = 0; i < n; ++i) {
		const auto &s1 = segments[i];
		points.emplace_back(s1.a);
		points.emplace_back(s1.b);

		for(int j = i + 1; j < n; ++j) {
			const auto &s2 = segments[j];
			if(intersect(s1, s2)) {
				points.emplace_back(crosspoint(s1, s2));
			}
		}
	}

	sort(points.begin(), points.end());
	points.erase(unique(points.begin(), points.end()), points.end());

	const int V = points.size();
	vector<vector<edge>> G(V);

	for(const auto &s : segments) {
		vector<pair<double, int>> vs;
		for(int i = 0; i < V; ++i) {
			if(intersect(s, points[i])) {
				vs.emplace_back(abs(s.a - points[i]), i);
			}
		}

		sort(vs.begin(), vs.end());
		for(int i = 1; i < static_cast<int>(vs.size()); ++i) {
			const int v = vs[i].second;
			const int u = vs[i - 1].second;
			G[v].emplace_back(u, angle(points[u] - points[v]));
			G[u].emplace_back(v, angle(points[v] - points[u]));
		}
	}

	for(auto &es : G) sort(es.begin(), es.end());
	return G;
}

vector<polygon> find_faces(const vector<point> &points, vector<vector<edge>> &G) {
	const int n = G.size();
	vector<polygon> res;

	rep(i, n) {
		rep(j, G[i].size()) {
			int v = i;
			int e_idx = j;
			polygon P;

			while(true) {
				edge &e = G[v][e_idx];
				if(e.used) break;

				e.used = true;
				P.emplace_back(points[v]);

				rep(k, G[e.to].size()) {
					if(G[e.to][k].to == v) {
						e_idx = (k + 1) % G[e.to].size();
						v = e.to;
						break;
					}
				}
			}

			if(P.size() >= 3) res.emplace_back(move(P));
		}
	}

	unsigned idx = 0;
	while(idx < res.size()) {
		const point diff = res[idx][1] - res[idx][0];
		assert(abs(diff) > EPS);
		const point p = res[idx][0] + diff * 0.5 + point(diff.y, -diff.x) * (2.0 * EPS / abs(diff));
		if(contain(res[idx], p)) {
			++idx;
		}
		else {
			res.erase(res.begin() + idx);
		}
	}

	return res;
}

int main() {
	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	int n;
	cin >> n;

	vector<segment> segments;
	segments.reserve(n);

	rep(i, n) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		segments.emplace_back(point(x1, y1), point(x2, y2));
	}

	vector<point> points;
	auto G = arrangement(segments, points);
	const auto faces = find_faces(points, G);

	const int num = faces.size();
	vector<vector<bool>> mat(num, vector<bool>(num, false));

	rep(i, num) {
		rep(j, i) {
			if(intersect(faces[i], faces[j])) {
				mat[i][j] = mat[j][i] = true;
			}
		}
	}

	int ans = 0;
	vector<bool> used(num, false);
	rep(i, num) {
		if(used[i]) continue;

		++ans;
		used[i] = true;
		queue<int> que;
		que.push(i);

		while(!que.empty()) {
			const int v = que.front();
			que.pop();

			rep(j, num) {
				if(!used[j] && mat[v][j]) {
					que.push(j);
					used[j] = true;
				}
			}
		}
	}
	
	cout << ans << endl;

	return EXIT_SUCCESS;
}
accuracy: 1
time_ms: 20
memory_kb: 1448
final_rank: 1

code_snippet: #include <algorithm>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

#define REP(i, a, b) for(int i = (a); i < int(b); ++i)
#define rep(i, n) REP(i, 0, n)
#define ALL(x) begin(x), end(x)

constexpr double EPS = 1e-8;

struct point {
	double x, y;
	explicit point(double x_ = 0, double y_ = 0):x(x_), y(y_) {}
	point(const point &p):x(p.x), y(p.y) {}

	inline point operator+(const point &p) const {
		return point(x + p.x, y + p.y);
	}

	inline point operator-(const point &p) const {
		return point(x - p.x, y - p.y);
	}

	inline point operator*(double s) const {
		return point(x * s, y * s);
	}

	inline bool operator<(const point &p) const {
		return x + EPS < p.x || (abs(x - p.x) < EPS && y + EPS < p.y);
	}

	inline bool operator==(const point &p) const {
		return abs(x - p.x) < EPS && abs(y - p.y) < EPS;
	}
};

typedef vector<point> polygon;

struct segment {
	point a, b;
	segment(const point &a_, const point &b_):a(a_), b(b_) {}
};

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

inline double norm(const point &p) {
	return p.x * p.x + p.y * p.y;
}

inline double abs(const point &p) {
	return sqrt(norm(p));
}

inline double angle(const point &p) {
	return atan2(p.y, p.x);
}

inline int ccw(const point &a, point b, point c) {
	b = b - a;
	c = c - a;
	const double tmp = cross(b, c);
	if(tmp > EPS) return 1; // ccw
	if(tmp < -EPS) return -1; // cw
	if(dot(b, c) < 0) return 2; // c, a, b 順に一直線上
	if(norm(b) < norm(c)) return -2; // a, b, c 順に一直線上
	return 0; //a, c, b 順で一直線上
}

inline bool intersect(const segment &s, const segment &t) {
	const int c1 = ccw(s.a, s.b, t.a);
	const int c2 = ccw(s.a, s.b, t.b);
	const int c3 = ccw(t.a, t.b, s.a);
	const int c4 = ccw(t.a, t.b, s.b);
	return (c1 * c2 < 0 || (c1 == 0 && c1 == c2)) && (c3 * c4 < 0 || (c3 == 0 && c3 == c4));
}

inline bool intersect(const segment &s, const point &p) {
	return ccw(s.a, s.b, p) == 0;
}

inline point crosspoint(const segment &s, const segment &t) {
	const double tmp = cross(s.b - s.a, t.b - t.a);
	if(abs(tmp) < EPS) { // 一直線上
		if(intersect(s, t.a)) return t.a;
		if(intersect(s, t.b)) return t.b;
		if(intersect(t, s.a)) return s.a;
		return s.b;
	}
	return t.a + (t.b - t.a) * cross(s.b - s.a, s.b - t.a) * (1.0 / tmp);
}

inline int sign(double d) {
	return d > EPS ? 1 : d < -EPS ? -1 : 0;
}

inline int judge(const point &a, const point &b, const point &c) {
	return sign(cross(a, c)) - sign(cross(b, c)) - sign(cross(a, b));
}

inline bool contain(const polygon &P, const point &p) {
	const int n = P.size();
	bool res = false;
	rep(i, n) {
		point v1 = P[i] - p, v2 = P[(i + 1) % n] - p;
		if(v1.y > v2.y) swap(v1, v2);
		if(v1.y < EPS && EPS < v2.y && cross(v1, v2) > EPS) res = !res;
		if(abs(cross(v1, v2)) < EPS && dot(v1, v2) < EPS) return false;
	}
	return res;
}

inline bool intersect(const polygon &P, const segment &s) {
	const int n = P.size();
	if(contain(P, s.a) || contain(P, s.b)) return true;

	for(int i = 0; i < n; ++i) {
		const auto &curr = P[i];
		const auto &next = P[(i + 1) % n];
		if(intersect(s, segment(curr, next))) return true;
	}

	return false;
}

inline bool intersect(const polygon &A, const polygon &B) {
	point prev = B.back();

	for(const auto &curr : B) {
		if(intersect(A, segment(prev, curr))) return true;
		prev = curr;
	}

	for(const auto &p : A) {
		if(contain(B, p)) return true;
	}
	
	return false;
}

struct edge {
	int to;
	double theta;
	bool used;
	edge(int to_, double theta_):to(to_), theta(theta_), used(false) {}

	inline bool operator<(const edge &e) const {
		return theta < e.theta;
	}
};

vector<vector<edge>> arrangement(const vector<segment> &segments, vector<point> &points) {
	const int n = segments.size();
	points.clear();

	for(int i = 0; i < n; ++i) {
		const auto &s1 = segments[i];
		points.emplace_back(s1.a);
		points.emplace_back(s1.b);

		for(int j = i + 1; j < n; ++j) {
			const auto &s2 = segments[j];
			if(intersect(s1, s2)) {
				points.emplace_back(crosspoint(s1, s2));
			}
		}
	}

	sort(points.begin(), points.end());
	points.erase(unique(points.begin(), points.end()), points.end());

	const int V = points.size();
	vector<vector<edge>> G(V);

	for(const auto &s : segments) {
		vector<pair<double, int>> vs;
		for(int i = 0; i < V; ++i) {
			if(intersect(s, points[i])) {
				vs.emplace_back(abs(s.a - points[i]), i);
			}
		}

		sort(vs.begin(), vs.end());
		for(int i = 1; i < static_cast<int>(vs.size()); ++i) {
			const int v = vs[i].second;
			const int u = vs[i - 1].second;
			G[v].emplace_back(u, angle(points[u] - points[v]));
			G[u].emplace_back(v, angle(points[v] - points[u]));
		}
	}

	for(auto &es : G) sort(es.begin(), es.end());
	return G;
}

vector<polygon> find_faces(const vector<point> &points, vector<vector<edge>> &G) {
	const int n = G.size();
	vector<polygon> res;

	rep(i, n) {
		rep(j, G[i].size()) {
			int v = i;
			int e_idx = j;
			polygon P;

			while(true) {
				edge &e = G[v][e_idx];
				if(e.used) break;

				e.used = true;
				P.emplace_back(points[v]);

				rep(k, G[e.to].size()) {
					if(G[e.to][k].to == v) {
						e_idx = (k + 1) % G[e.to].size();
						v = e.to;
						break;
					}
				}
			}

			if(P.size() >= 3) res.emplace_back(move(P));
		}
	}

	unsigned idx = 0;
	while(idx < res.size()) {
		const point diff = res[idx][1] - res[idx][0];
		const point p = res[idx][0] + diff * 0.5 + point(diff.y, -diff.x) * (2.0 * EPS / abs(diff));
		if(contain(res[idx], p)) {
			++idx;
		}
		else {
			res.erase(res.begin() + idx);
		}
	}

	return res;
}

int main() {
	cin.tie(nullptr);
	ios::sync_with_stdio(false);

	int n;
	cin >> n;

	vector<segment> segments;
	segments.reserve(n);

	rep(i, n) {
		int x1, y1, x2, y2;
		cin >> x1 >> y1 >> x2 >> y2;
		segments.emplace_back(point(x1, y1), point(x2, y2));
	}

	vector<point> points;
	auto G = arrangement(segments, points);
	const auto faces = find_faces(points, G);

	const int num = faces.size();
	vector<vector<bool>> mat(num, vector<bool>(num, false));

	rep(i, num) {
		rep(j, i) {
			if(intersect(faces[i], faces[j])) {
				mat[i][j] = mat[j][i] = true;
			}
		}
	}

	int ans = 0;
	vector<bool> used(n, false);
	rep(i, num) {
		if(used[i]) continue;

		++ans;
		used[i] = true;
		queue<int> que;
		que.push(i);

		while(!que.empty()) {
			const int v = que.front();
			que.pop();

			rep(j, num) {
				if(!used[j] && mat[v][j]) {
					que.push(j);
					used[j] = true;
				}
			}
		}
	}
	
	cout << ans << endl;

	return EXIT_SUCCESS;
}
accuracy: 0.6
time_ms: 30
memory_kb: 1552
final_rank: 20

code_snippet: #include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define EQ(a,b) (abs((a)-(b)) < EPS)
#define fs first
#define sc second
#define pb push_back
#define sz size()
#define all(a) (a).begin(),(a).end()

using namespace std;
typedef double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Poly;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<D,int> pdi;

const D EPS = 1e-8;
const D PI = acos(-1);

namespace std{
  bool operator<(const P &a,const P &b){
    return EQ(real(a),real(b))?imag(a)<imag(b):real(a)<real(b);
  }
  bool operator==(const P &a, const P &b){return EQ(a,b);}
};

inline D dot(P x,P y){return real(conj(x)*y);}
inline D cross(P x,P y){return imag(conj(x)*y);}

inline int ccw(P a,P b,P c){
  b -= a;c -= a;
  if (cross(b,c)>EPS) return 1;   //counter clockwise
  if (cross(b,c)<-EPS) return -1; //clockwise
  if (dot(b, c)<-EPS) return 2;   //c--a--b on line
  if (abs(b)+EPS<abs(c)) return -2;   //a--b--c on line
  return 0;                       //on segment
}

inline P line_cp(L a,L b){
  return a.fs+(a.sc-a.fs)*cross(b.sc-b.fs,b.fs-a.fs)/cross(b.sc-b.fs,a.sc-a.fs);
}
inline bool is_cp(L a,L b){
  if(ccw(a.fs,a.sc,b.fs)*ccw(a.fs,a.sc,b.sc)<=0)
    if(ccw(b.fs,b.sc,a.fs)*ccw(b.fs,b.sc,a.sc)<=0)return true;
  return false;
}

inline  bool para(L a,L b){return abs(cross(a.fs-a.sc,b.fs-b.sc))<EPS;}

inline bool in_poly(Poly p,P x){
  if(p.empty())return false;

  int n = p.size();
  D xMax = x.real();
  rep(i,n){
    if(xMax < p[i].real())xMax = p[i].real();
    if(EQ(x,p[i]))return false;
  }
  L h = L( x,P(xMax + 1.0, x.imag()) );

  int c = 0;
  rep(i,n){
    L l = L(p[i],p[(i+1)%n]);
    if(!para(h,l) && is_cp(h,l)){
      P cp = line_cp(h,l);
      if(cp.real() < x.real() + EPS)continue;
      if(!EQ(cp, (l.fs.imag() < l.sc.imag())?l.sc:l.fs))c++;
    }
  }

  return (c&1)?true:false;
}

//b divides a into segments
inline vector<L> seg_cut(L a, L b){
  vector<L> res;
  
  if(!is_cp(a,b))res.pb(a);
  else{
    if(para(a,b)){
      int cpf = ccw(a.fs,a.sc,b.fs), cps = ccw(a.fs,a.sc,b.sc);
      if(!cpf && !cps){
	if(ccw(a.fs,b.fs,b.sc)==0){
	  res.pb(L(a.fs,b.sc)); res.pb(L(b.sc,b.fs)); res.pb(L(b.fs,a.sc));
	}else{
	  res.pb(L(a.fs,b.fs)); res.pb(L(b.fs,b.sc)); res.pb(L(b.sc,a.sc));
	}
      }else if(!cpf){
	res.pb(L(a.fs,b.fs)); res.pb(L(a.sc,b.fs));
      }else if(!cps){
	res.pb(L(a.fs,b.sc)); res.pb(L(a.sc,b.sc));
      }else res.pb(a);
    }else{
      P cp = line_cp(a,b);
      if(!EQ(a.fs,cp) && !EQ(a.sc,cp)){
	res.pb(L(a.fs,cp)); res.pb(L(a.sc,cp));
      }else{
	res.pb(a);
      }
    }
  }
  return res;
}

inline vector<L> seg_set_cut(vector<L> l){
  vector< vector<L> > vl(l.sz);
  rep(i,l.sz)vl[i].pb(l[i]);
  
  rep(i,l.sz)for(int j=i+1;j<(int)l.sz;j++){
    rep(ii,vl[i].sz)rep(jj,vl[j].sz){
      if(is_cp(vl[i][ii],vl[j][jj])){
	vector<L> a = seg_cut(vl[i][ii],vl[j][jj]);
	vector<L> b = seg_cut(vl[j][jj],vl[i][ii]);

	while(a.sz>1){vl[i].pb(a.back()); a.pop_back();}
	vl[i][ii] = a[0];
	while(b.sz>1){vl[j].pb(b.back()); b.pop_back();}
	vl[j][jj] = b[0];
	goto END;
      }
    }
    END:;
  }

  vector<L> res;
  rep(i,l.sz)rep(j,vl[i].size()){
    if(vl[i][j].sc<vl[i][j].fs)swap(vl[i][j].fs,vl[i][j].sc);
    res.pb(vl[i][j]);
  }
  sort(all(res));
  res.erase(unique(all(res)),res.end());
  return res;
}

inline void seg_arrange(vector<L> input_segs, vector<P> &vp,
			vector<pii> &ve, vector< vector<pii> > &graph){
  vector<L> vl = seg_set_cut(input_segs);

  vp.clear();
  rep(i,vl.sz){
    vp.pb(vl[i].fs);
    vp.pb(vl[i].sc);
  }
  sort(all(vp));
  vp.erase(unique(all(vp)),vp.end());

  ve.resize(vl.sz);
  vector< vector<pdi> > edges(vp.sz);

  rep(i,vl.sz){
    int s = find(all(vp),vl[i].fs)-vp.begin();
    int t = find(all(vp),vl[i].sc)-vp.begin();
    ve[i] = pii(s,t);
    edges[s].push_back(pdi(arg(vp[t]-vp[s]),i));
    edges[t].push_back(pdi(arg(vp[s]-vp[t]),i));
  }

  graph.resize(vp.sz);
  rep(i,vp.sz){
    sort(all(edges[i]));
    graph[i].clear();
    rep(j,edges[i].sz){
      int id = edges[i][j].sc;
      int v = (ve[id].fs == i)?ve[id].sc:ve[id].fs;
      graph[i].pb(pii(v,id));
    }
  }
}

vector<int> id;
vector<vi> g;

inline vector<Poly> extract_poly(vector<L> set_l){
  vector<P> p;
  vector<pii> l;
  vector< vector<pii> > e;
  seg_arrange(set_l,p,l,e);

  g = vector<vi>(l.sz,vi(2,-1));
  vector<Poly> PolySet;
  id.clear();

  rep(i,l.sz)rep(j,2){
    int cur = i, dir = j;
    if(g[cur][dir]>=0)continue;

    D area = 0;
    Poly poly;
    while(true){
      g[cur][dir] = id.sz;
      
      int node = dir?l[cur].fs:l[cur].sc;
      int prv = dir?l[cur].sc:l[cur].fs;
      poly.push_back(p[node]);
      area += cross(p[node],p[prv]);

      int nxt = find(all(e[node]),pii(prv,cur))-e[node].begin();
      cur = e[node][(nxt+1)%e[node].sz].sc;
      dir = (l[cur].fs==node)?0:1;

      if(g[cur][dir]>=0)break;
    }
    
    if(area>EPS){
      id.pb(PolySet.sz);
      PolySet.pb(poly);
    }else{
      id.pb(-1);
    }
  }
  return PolySet;
}


//union-find
vi uf,rank;
void init(int n){
  uf.resize(n); rank.resize(n);
  rep(i,n)uf[i] = i, rank[i] = 0;
}

int find(int a){
  if(uf[a]==a)return a;
  return uf[a] = find(uf[a]);
}

int unite(int a, int b){
  a = find(a); b = find(b);
  if(a==b)return 0;
  if(rank[a]<rank[b])uf[a] = b;
  else{
    uf[b] = a;
    if(rank[a]==rank[b])rank[a]++;
  }
  return 1;
}

int main(){
  int n;
  cin >> n;
  vector<L> seg(n);

  rep(i,n){
    int x,y;
    cin >> x >> y; seg[i].fs = P(x,y);
    cin >> x >> y; seg[i].sc = P(x,y);
  }
  
  vector<Poly> PolySet = extract_poly(seg);

  int ans = PolySet.size();
  init(PolySet.size());

  rep(i,g.size()){
    int a = id[g[i][0]], b = id[g[i][1]];
    if(a<0 || b<0)continue;
    ans -= unite(a,b);
  }

  rep(i,PolySet.size()){
    rep(j,PolySet.size()){
      if(find(i)==find(j))continue;
      bool f = true;
      rep(k,PolySet[i].size()){
	f &= in_poly(PolySet[j],PolySet[i][k]);
	if(!f)break;
      }
      if(f)ans -= unite(i,j);
    }
  }

  cout << ans << endl;
}
accuracy: 1
time_ms: 10
memory_kb: 1516
final_rank: 3

code_snippet: #include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define EQ(a,b) (abs((a)-(b)) < EPS)
#define fs first
#define sc second
#define all(a) (a).begin(),(a).end()
using namespace std;
typedef double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Poly;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<D,int> pdi;

const D EPS = 1e-8;
const D PI = acos(-1);

namespace std{
  bool operator<(const P &a,const P &b){
    return EQ(real(a),real(b))?imag(a)<imag(b):real(a)<real(b);
  }
  bool operator==(const P &a, const P &b){return EQ(a,b);}
}

inline D dot(P x,P y){return real(conj(x)*y);}
inline D cross(P x,P y){return imag(conj(x)*y);}

inline int ccw(P a,P b,P c){
  b -= a;c -= a;
  if (cross(b,c)>EPS) return 1;   //counter clockwise
  if (cross(b,c)<-EPS) return -1; //clockwise
  if (dot(b, c)<-EPS) return 2;   //c--a--b on line
  if (abs(b)+EPS<abs(c)) return -2;   //a--b--c on line
  return 0;                       //on segment
}

inline bool is_cp(L a,L b){
  if(ccw(a.fs,a.sc,b.fs)*ccw(a.fs,a.sc,b.sc)<=0)
    if(ccw(b.fs,b.sc,a.fs)*ccw(b.fs,b.sc,a.sc)<=0)return true;
  return false;
}

inline P seg_cp(L a,L b){
  double d = abs(cross(b.sc-b.fs,a.fs-b.fs));
  return a.fs + (a.sc-a.fs)*( d /(d + abs(cross(b.sc-b.fs,a.sc-b.fs))) );
}

inline  bool para(L a,L b){return abs(cross(a.fs-a.sc,b.fs-b.sc))<EPS;}

inline bool in_poly(Poly p,P x){
  if(p.empty())return false;

  int s = p.size();
  D xMax = x.real();
  rep(i,s){
    if(xMax < p[i].real())xMax = p[i].real();
    if(EQ(x,p[i]))return false;
  }
  L h = L( x,P(xMax + 1.0, x.imag()) );

  int c = 0;
  rep(i,s){
    L l = L(p[i],p[(i+1)%s]);
    if(!para(h,l) && is_cp(h,l)){
      P cp = seg_cp(h,l);
      if(cp.real() < x.real() + EPS)continue;
      if(!EQ(cp, (l.fs.imag() < l.sc.imag())?l.sc:l.fs))c++;
    }
  }

  return (c&1)?true:false;
}

inline vector<L> seg_set_cut(vector<L> l){
  int card = l.size();
  vector< vector<L> > vl(card);
  rep(i,card)vl[i].push_back(l[i]);
  
  rep(i,card)for(int j=i+1;j<card;j++){
    rep(ii,vl[i].size())rep(jj,vl[j].size()){
      if(is_cp(vl[i][ii],vl[j][jj])){
	P cp = seg_cp(vl[i][ii],vl[j][jj]);
	if(!EQ(vl[i][ii].fs,cp) && !EQ(vl[i][ii].sc,cp)){
	  vl[i].push_back(L(vl[i][ii].sc,cp));
	  vl[i][ii] = L(vl[i][ii].fs,cp);
	}
	if(!EQ(vl[j][jj].fs,cp) && !EQ(vl[j][jj].sc,cp)){
	  vl[j].push_back(L(vl[j][jj].sc,cp));
	  vl[j][jj] = L(vl[j][jj].fs,cp);
	}
	goto END;
      }
    }
  END:;
  }
  vector<L> res;
  rep(i,card){
    rep(j,vl[i].size())res.push_back(vl[i][j]);
  }
  return res;
}

//union-find
vi uf,rank;
void init(int n){
  uf.resize(n); rank.resize(n);
  rep(i,n)uf[i] = i, rank[i] = 0;
}

int find(int a){
  if(uf[a]==a)return a;
  return uf[a] = find(uf[a]);
}

int unite(int a, int b){
  a = find(a); b = find(b);
  if(a==b)return 0;
  if(rank[a]<rank[b])uf[a] = b;
  else{
    uf[b] = a;
    if(rank[a]==rank[b])rank[a]++;
  }
  return 1;
}

int main(){
  int n;
  cin >> n;
  vector<L> seg(n);

  rep(i,n){
    int x,y;
    cin >> x >> y; seg[i].fs = P(x,y);
    cin >> x >> y; seg[i].sc = P(x,y);
  }

  vector<L> SegSet = seg_set_cut(seg);

  vector<P> p;
  rep(i,SegSet.size()){
    p.push_back(SegSet[i].fs);
    p.push_back(SegSet[i].sc);
  }
  sort(all(p));
  p.erase(unique(all(p)),p.end());

  vector<pii> l(SegSet.size());
  vector< vector<pdi> > wall(p.size());
 
  rep(i,SegSet.size()){
    int s = lower_bound(all(p),SegSet[i].fs)-p.begin();
    int t = lower_bound(all(p),SegSet[i].sc)-p.begin();
    l[i] = pii(s,t);
    wall[s].push_back(pdi(arg(p[t]-p[s]),i));
    wall[t].push_back(pdi(arg(p[s]-p[t]),i));
  }

  rep(i,p.size())sort(all(wall[i]));

  vector<vi> g(l.size(),vi(2,-1));
  vector<Poly> PolySet;
  vi id;

  rep(i,l.size()){
    rep(j,2){
      int cur = i, dir = j;
      if(g[cur][dir]>=0)continue;

      D area = 0;
      Poly poly;
      while(true){
	g[cur][dir] = id.size();

	int node = dir?l[cur].fs:l[cur].sc;
	int prv = dir?l[cur].sc:l[cur].fs;
	poly.push_back(p[node]);
	area += cross(p[node],p[prv]);

	pdi seta = pdi(arg(p[prv]-p[node]),cur);
	int nxt = lower_bound(all(wall[node]),seta)-wall[node].begin();
	cur = wall[node][(nxt+1)%wall[node].size()].sc;
	dir = ((l[cur].fs==node)?0:1);

	if(g[cur][dir]>=0)break;
      }

      if(area>EPS){
	id.push_back(PolySet.size());
	PolySet.push_back(poly);
      }else{
	id.push_back(-1);
      }
    }
  }

  int ans = PolySet.size();
  init(PolySet.size());

  rep(i,PolySet.size()){
    rep(j,PolySet.size()){
      if(i==j)continue;
      bool f = true;
      rep(k,PolySet[i].size()){
	f &= in_poly(PolySet[j],PolySet[i][k]);
	if(!f)break;
      }
      if(f)ans -= unite(i,j);
    }
  }

  rep(i,l.size()){
    int a = id[g[i][0]], b = id[g[i][1]];
    if(a<0 || b<0)continue;
    ans -= unite(a,b);
  }

  cout << ans << endl;
}
accuracy: 1
time_ms: 20
memory_kb: 1544
final_rank: 4

code_snippet: #include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define EQ(a,b) (abs((a)-(b)) < EPS)
#define fs first
#define sc second
#define all(a) (a).begin(),(a).end()
using namespace std;
typedef long double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Poly;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<D,int> pdi;

const D EPS = 1e-10L;
const D PI = acos(-1);

namespace std{
  bool operator<(const P &a,const P &b){
    return EQ(real(a),real(b))?imag(a)<imag(b):real(a)<real(b);
  }
  bool operator==(const P &a, const P &b){return EQ(a,b);}
}

inline D dot(P x,P y){return real(conj(x)*y);}
inline D cross(P x,P y){return imag(conj(x)*y);}

inline int ccw(P a,P b,P c){
  b -= a;c -= a;
  if (cross(b,c)>EPS) return 1;   //counter clockwise
  if (cross(b,c)<-EPS) return -1; //clockwise
  if (dot(b, c)<-EPS) return 2;   //c--a--b on line
  if (abs(b)+EPS<abs(c)) return -2;   //a--b--c on line
  return 0;                       //on segment
}

inline bool is_cp(L a,L b){
  if(ccw(a.fs,a.sc,b.fs)*ccw(a.fs,a.sc,b.sc)<=0)
    if(ccw(b.fs,b.sc,a.fs)*ccw(b.fs,b.sc,a.sc)<=0)return true;
  return false;
}

inline P seg_cp(L a,L b){
  double d = abs(cross(b.sc-b.fs,a.fs-b.fs));
  return a.fs + (a.sc-a.fs)*( d /(d + abs(cross(b.sc-b.fs,a.sc-b.fs))) );
}

inline  bool para(L a,L b){return abs(cross(a.fs-a.sc,b.fs-b.sc))<EPS;}

inline bool in_poly(Poly p,P x){
  if(p.empty())return false;

  int s = p.size();
  D xMax = x.real();
  rep(i,s){
    if(xMax < p[i].real())xMax = p[i].real();
    if(EQ(x,p[i]))return false;
  }
  L h = L( x,P(xMax + 1.0, x.imag()) );

  int c = 0;
  rep(i,s){
    L l = L(p[i],p[(i+1)%s]);
    if(!para(h,l) && is_cp(h,l)){
      P cp = seg_cp(h,l);
      if(cp.real() < x.real() + EPS)continue;
      if(!EQ(cp, (l.fs.imag() < l.sc.imag())?l.sc:l.fs))c++;
    }
  }

  return (c&1)?true:false;
}

inline vector<L> seg_set_cut(vector<L> l){
  int card = l.size();
  vector< vector<L> > vl(card);
  rep(i,card)vl[i].push_back(l[i]);
  
  rep(i,card)for(int j=i+1;j<card;j++){
    rep(ii,vl[i].size())rep(jj,vl[j].size()){
      if(is_cp(vl[i][ii],vl[j][jj])){
	P cp = seg_cp(vl[i][ii],vl[j][jj]);
	if(!EQ(vl[i][ii].fs,cp) && !EQ(vl[i][ii].sc,cp)){
	  vl[i].push_back(L(vl[i][ii].sc,cp));
	  vl[i][ii] = L(vl[i][ii].fs,cp);
	}
	if(!EQ(vl[j][jj].fs,cp) && !EQ(vl[j][jj].sc,cp)){
	  vl[j].push_back(L(vl[j][jj].sc,cp));
	  vl[j][jj] = L(vl[j][jj].fs,cp);
	}
	goto END;
      }
    }
  END:;
  }
  vector<L> res;
  rep(i,card){
    rep(j,vl[i].size())res.push_back(vl[i][j]);
  }
  return res;
}

//union-find
vi uf,rank;
void init(int n){
  uf.resize(n); rank.resize(n);
  rep(i,n)uf[i] = i, rank[i] = 0;
}

int find(int a){
  if(uf[a]==a)return a;
  return uf[a] = find(uf[a]);
}

int unite(int a, int b){
  a = find(a); b = find(b);
  if(a==b)return 0;
  if(rank[a]<rank[b])uf[a] = b;
  else{
    uf[b] = a;
    if(rank[a]==rank[b])rank[a]++;
  }
  return 1;
}

int main(){
  int n;
  cin >> n;
  vector<L> seg(n);

  rep(i,n){
    int x,y;
    cin >> x >> y; seg[i].fs = P(x,y);
    cin >> x >> y; seg[i].sc = P(x,y);
  }

  vector<L> SegSet = seg_set_cut(seg);

  vector<P> p;
  rep(i,SegSet.size()){
    p.push_back(SegSet[i].fs);
    p.push_back(SegSet[i].sc);
  }
  sort(all(p));
  p.erase(unique(all(p)),p.end());

  vector<pii> l(SegSet.size());
  vector< vector<pdi> > wall(p.size());
 
  rep(i,SegSet.size()){
    int s = lower_bound(all(p),SegSet[i].fs)-p.begin();
    int t = lower_bound(all(p),SegSet[i].sc)-p.begin();
    l[i] = pii(s,t);
    wall[s].push_back(pdi(arg(p[t]-p[s]),i));
    wall[t].push_back(pdi(arg(p[s]-p[t]),i));
  }

  rep(i,p.size())sort(all(wall[i]));

  vector<vi> g(l.size(),vi(2,-1));
  vector<Poly> PolySet;
  vi id;

  rep(i,l.size()){
    rep(j,2){
      int cur = i, dir = j;
      if(g[cur][dir]>=0)continue;

      D area = 0;
      Poly poly;
      while(true){
	g[cur][dir] = id.size();

	int node = dir?l[cur].fs:l[cur].sc;
	int prv = dir?l[cur].sc:l[cur].fs;
	poly.push_back(p[node]);
	area += cross(p[node],p[prv]);

	pdi seta = pdi(arg(p[prv]-p[node]),cur);
	int nxt = lower_bound(all(wall[node]),seta)-wall[node].begin();
	cur = wall[node][(nxt+1)%wall[node].size()].sc;
	dir = ((l[cur].fs==node)?0:1);

	if(g[cur][dir]>=0)break;
      }

      if(area>EPS){
	id.push_back(PolySet.size());
	PolySet.push_back(poly);
      }else{
	id.push_back(-1);
      }
    }
  }

  int ans = PolySet.size();
  init(PolySet.size());

  rep(i,PolySet.size()){
    rep(j,PolySet.size()){
      if(i==j)continue;
      bool f = true;
      rep(k,PolySet[i].size()){
	f &= in_poly(PolySet[j],PolySet[i][k]);
	if(!f)break;
      }
      if(f)ans -= unite(i,j);
    }
  }

  rep(i,l.size()){
    int a = id[g[i][0]], b = id[g[i][1]];
    if(a<0 || b<0)continue;
    ans -= unite(a,b);
  }

  cout << ans << endl;
}
accuracy: 1
time_ms: 70
memory_kb: 1584
final_rank: 6

code_snippet: #include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define EQ(a,b) (abs((a)-(b)) < EPS)
#define fs first
#define sc second
#define all(a) (a).begin(),(a).end()
using namespace std;
typedef long double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Poly;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<D,int> pdi;

const D EPS = 1e-10L;
const D PI = acos(-1);

namespace std{
  bool operator<(const P &a,const P &b){
    return EQ(real(a),real(b))?imag(a)<imag(b):real(a)<real(b);
  }
  bool operator==(const P &a, const P &b){return EQ(a,b);}
}

inline D dot(P x,P y){return real(conj(x)*y);}
inline D cross(P x,P y){return imag(conj(x)*y);}

inline int ccw(P a,P b,P c){
  b -= a;c -= a;
  if (cross(b,c)>EPS) return 1;   //counter clockwise
  if (cross(b,c)<-EPS) return -1; //clockwise
  if (dot(b, c)<-EPS) return 2;   //c--a--b on line
  if (abs(b)+EPS<abs(c)) return -2;   //a--b--c on line
  return 0;                       //on segment
}

inline bool is_cp(L a,L b){
  if(ccw(a.fs,a.sc,b.fs)*ccw(a.fs,a.sc,b.sc)<=0)
    if(ccw(b.fs,b.sc,a.fs)*ccw(b.fs,b.sc,a.sc)<=0)return true;
  return false;
}

inline P seg_cp(L a,L b){
  double d = abs(cross(b.sc-b.fs,a.fs-b.fs));
  return a.fs + (a.sc-a.fs)*( d /(d + abs(cross(b.sc-b.fs,a.sc-b.fs))) );
}

inline  bool para(L a,L b){return abs(cross(a.fs-a.sc,b.fs-b.sc))<EPS;}

inline bool in_poly(Poly p,P x){
  if(p.empty())return false;

  int s = p.size();
  D xMax = x.real();
  rep(i,s){
    if(xMax < p[i].real())xMax = p[i].real();
    if(EQ(x,p[i]))return false;
  }
  L h = L( x,P(xMax + 1.0, x.imag()) );

  int c = 0;
  rep(i,s){
    L l = L(p[i],p[(i+1)%s]);
    if(!para(h,l) && is_cp(h,l)){
      P cp = seg_cp(h,l);
      if(cp.real() < x.real() + EPS)continue;
      if(!EQ(cp, (l.fs.imag() < l.sc.imag())?l.sc:l.fs))c++;
    }
  }

  return (c&1)?true:false;
}

inline vector<L> seg_set_cut(vector<L> l){
  int card = l.size();
  vector< vector<L> > vl(card);
  rep(i,card)vl[i].push_back(l[i]);
  
  rep(i,card)for(int j=i+1;j<card;j++){
    rep(ii,vl[i].size())rep(jj,vl[j].size()){
      if(is_cp(vl[i][ii],vl[j][jj])){
	P cp = seg_cp(vl[i][ii],vl[j][jj]);
	if(!EQ(vl[i][ii].fs,cp) && !EQ(vl[i][ii].sc,cp)){
	  vl[i].push_back(L(vl[i][ii].sc,cp));
	  vl[i][ii] = L(vl[i][ii].fs,cp);
	}
	if(!EQ(vl[j][jj].fs,cp) && !EQ(vl[j][jj].sc,cp)){
	  vl[j].push_back(L(vl[j][jj].sc,cp));
	  vl[j][jj] = L(vl[j][jj].fs,cp);
	}
	goto END;
      }
    }
  END:;
  }
  vector<L> res;
  rep(i,card){
    rep(j,vl[i].size())res.push_back(vl[i][j]);
  }
  return res;
}

//union-find
vi uf,rank;
void init(int n){
  uf.resize(n); rank.resize(n);
  rep(i,n)uf[i] = i, rank[i] = 0;
}

int find(int a){
  if(uf[a]==a)return a;
  return uf[a] = find(uf[a]);
}

int unite(int a, int b){
  a = find(a); b = find(b);
  if(a==b)return 0;
  if(rank[a]<rank[b])uf[a] = b;
  else{
    uf[b] = a;
    if(rank[a]==rank[b])rank[a]++;
  }
  return 1;
}

int main(){
  int n;
  cin >> n;
  vector<L> seg(n);

  rep(i,n){
    int x,y;
    cin >> x >> y; seg[i].fs = P(x,y);
    cin >> x >> y; seg[i].sc = P(x,y);
  }

  vector<L> SegSet = seg_set_cut(seg);

  vector<P> p;
  rep(i,SegSet.size()){
    p.push_back(SegSet[i].fs);
    p.push_back(SegSet[i].sc);
  }
  sort(all(p));
  p.erase(unique(all(p)),p.end());

  vector<pii> l(SegSet.size());
  vector< vector<pdi> > wall(p.size());
 
  rep(i,SegSet.size()){
    int s = lower_bound(all(p),SegSet[i].fs)-p.begin();
    int t = lower_bound(all(p),SegSet[i].sc)-p.begin();
    l[i] = pii(s,t);
    wall[s].push_back(pdi(arg(p[t]-p[s]),i));
    wall[t].push_back(pdi(arg(p[s]-p[t]),i));
  }

  rep(i,p.size())sort(all(wall[i]));

  vector<vi> g(l.size(),vi(2,-1));
  vector<Poly> PolySet;
  vi id;

  rep(i,l.size()){
    rep(j,2){
      int cur = i, dir = j;
      if(g[cur][dir]>=0)continue;

      D area = 0;
      Poly poly;
      while(true){
	g[cur][dir] = id.size();
	poly.push_back(dir?p[l[cur].fs]:p[l[cur].sc]);

	int node = dir?l[cur].fs:l[cur].sc;
	int prv = dir?l[cur].sc:l[cur].fs;
	area += cross(p[node],p[prv]);

	pdi seta = pdi(arg(p[prv]-p[node]),cur);
	int nxt = lower_bound(all(wall[node]),seta)-wall[node].begin();
	cur = wall[node][(nxt+1)%wall[node].size()].sc;
	dir = ((l[cur].fs==node)?0:1);

	/*
	rep(k,l.size()){
	  if(prv == l[k].fs && node == l[k].sc){
	    cur = k; dir = 0; break;
	  }
	  if(prv == l[k].sc && node == l[k].fs){
	    cur = k; dir = 1;  break;
	  }
	}
	*/
	if(g[cur][dir]>=0)break;
      }
      if(area>EPS){
	id.push_back(PolySet.size());
	PolySet.push_back(poly);
      }else{
	id.push_back(-1);
      }
    }
  }

  int ans = PolySet.size();
  init(PolySet.size());

  rep(i,PolySet.size()){
    rep(j,PolySet.size()){
      if(i==j)continue;
      bool f = true;
      rep(k,PolySet[i].size()){
	f &= in_poly(PolySet[j],PolySet[i][k]);
	if(!f)break;
      }
      if(f)ans -= unite(i,j);
    }
  }

  rep(i,l.size()){
    int a = id[g[i][0]], b = id[g[i][1]];
    if(a<0 || b<0)continue;
    ans -= unite(a,b);
  }

  cout << ans << endl;
}
accuracy: 1
time_ms: 70
memory_kb: 1580
final_rank: 5

code_snippet: #include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
#define EQ(a,b) (abs((a)-(b)) < EPS)
#define fs first
#define sc second
#define all(a) (a).begin(),(a).end()
using namespace std;
typedef long double D;
typedef complex<D> P;
typedef pair<P,P> L;
typedef vector<P> Poly;
typedef vector<int> vi;
typedef pair<int,int> pii;
typedef pair<D,int> pdi;

const D EPS = 1e-10L;
const D PI = acos(-1);

namespace std{
  bool operator<(const P &a,const P &b){
    return EQ(real(a),real(b))?imag(a)<imag(b):real(a)<real(b);
  }
  bool operator==(const P &a, const P &b){return EQ(a,b);}
}

inline D dot(P x,P y){return real(conj(x)*y);}
inline D cross(P x,P y){return imag(conj(x)*y);}

inline int ccw(P a,P b,P c){
  b -= a;c -= a;
  if (cross(b,c)>EPS) return 1;   //counter clockwise
  if (cross(b,c)<-EPS) return -1; //clockwise
  if (dot(b, c)<-EPS) return 2;   //c--a--b on line
  if (abs(b)+EPS<abs(c)) return -2;   //a--b--c on line
  return 0;                       //on segment
}

inline bool is_cp(L a,L b){
  if(ccw(a.fs,a.sc,b.fs)*ccw(a.fs,a.sc,b.sc)<=0)
    if(ccw(b.fs,b.sc,a.fs)*ccw(b.fs,b.sc,a.sc)<=0)return true;
  return false;
}

inline P seg_cp(L a,L b){
  double d = abs(cross(b.sc-b.fs,a.fs-b.fs));
  return a.fs + (a.sc-a.fs)*( d /(d + abs(cross(b.sc-b.fs,a.sc-b.fs))) );
}

inline  bool para(L a,L b){return abs(cross(a.fs-a.sc,b.fs-b.sc))<EPS;}

inline bool in_poly(Poly p,P x){
  if(p.empty())return false;

  int s = p.size();
  D xMax = x.real();
  rep(i,s){
    if(xMax < p[i].real())xMax = p[i].real();
    if(EQ(x,p[i]))return false;
  }
  L h = L( x,P(xMax + 1.0, x.imag()) );

  int c = 0;
  rep(i,s){
    L l = L(p[i],p[(i+1)%s]);
    if(!para(h,l) && is_cp(h,l)){
      P cp = seg_cp(h,l);
      if(cp.real() < x.real() + EPS)continue;
      if(!EQ(cp, (l.fs.imag() < l.sc.imag())?l.sc:l.fs))c++;
    }
  }

  return (c&1)?true:false;
}

inline vector<L> seg_set_cut(vector<L> l){
  int card = l.size();
  vector< vector<L> > vl(card);
  rep(i,card)vl[i].push_back(l[i]);
  
  rep(i,card)for(int j=i+1;j<card;j++){
    rep(ii,vl[i].size())rep(jj,vl[j].size()){
      if(is_cp(vl[i][ii],vl[j][jj])){
	P cp = seg_cp(vl[i][ii],vl[j][jj]);
	if(!EQ(vl[i][ii].fs,cp) && !EQ(vl[i][ii].sc,cp)){
	  vl[i].push_back(L(vl[i][ii].sc,cp));
	  vl[i][ii] = L(vl[i][ii].fs,cp);
	}
	if(!EQ(vl[j][jj].fs,cp) && !EQ(vl[j][jj].sc,cp)){
	  vl[j].push_back(L(vl[j][jj].sc,cp));
	  vl[j][jj] = L(vl[j][jj].fs,cp);
	}
	goto END;
      }
    }
  END:;
  }
  vector<L> res;
  rep(i,card){
    rep(j,vl[i].size())res.push_back(vl[i][j]);
  }
  return res;
}

//union-find
vi uf,rank;
void init(int n){
  uf.resize(n); rank.resize(n);
  rep(i,n)uf[i] = i, rank[i] = 0;
}

int find(int a){
  if(uf[a]==a)return a;
  return uf[a] = find(uf[a]);
}

int unite(int a, int b){
  a = find(a); b = find(b);
  if(a==b)return 0;
  if(rank[a]<rank[b])uf[a] = b;
  else{
    uf[b] = a;
    if(rank[a]==rank[b])rank[a]++;
  }
  return 1;
}

int main(){
  int n;
  cin >> n;
  vector<L> seg(n);

  rep(i,n){
    int x,y;
    cin >> x >> y; seg[i].fs = P(x,y);
    cin >> x >> y; seg[i].sc = P(x,y);
  }

  vector<L> SegSet = seg_set_cut(seg);

  vector<P> p;
  rep(i,SegSet.size()){
    p.push_back(SegSet[i].fs);
    p.push_back(SegSet[i].sc);
  }
  sort(all(p));
  p.erase(unique(all(p)),p.end());

  vector<pii> l(SegSet.size());
  vector< vector<pdi> > wall(p.size());
 
  rep(i,SegSet.size()){
    int s = lower_bound(all(p),SegSet[i].fs)-p.begin();
    int t = lower_bound(all(p),SegSet[i].sc)-p.begin();
    l[i] = pii(s,t);
    wall[s].push_back(pdi(arg(p[t]-p[s]),i));
    wall[t].push_back(pdi(arg(p[s]-p[t]),i));
  }

  rep(i,p.size())sort(all(wall[i]));

  vector<vi> g(l.size(),vi(2,-1));
  vector<Poly> PolySet;
  vi id;

  rep(i,l.size()){
    rep(j,2){
      int cur = i, dir = j;
      if(g[cur][dir]>=0)continue;

      D area = 0;
      Poly poly;
      while(true){
	g[cur][dir] = id.size();
	poly.push_back(dir?p[l[cur].fs]:p[l[cur].sc]);

	int node = dir?l[cur].fs:l[cur].sc;
	int prv = dir?l[cur].sc:l[cur].fs;
	pdi seta = pdi(arg(p[prv]-p[node]),0);
	int k = lower_bound(all(wall[node]),seta)-wall[node].begin();
	int edge = wall[node][(k+1)%wall[node].size()].sc;
	int nxt = (l[edge].fs==node)?l[edge].sc:l[edge].fs;
	area += cross(p[node],p[prv]);
	prv = node;
	node = nxt;

	/*
	rep(k,wall[node].size()){
	  if(wall[node][k].sc == prv){
	    int nxt = wall[node][(k+1)%wall[node].size()].sc;
	    area += cross(p[node],p[prv]);
	    prv = node;
	    node = nxt;
	    break;
	  }
	}
	*/
	
	rep(k,l.size()){
	  if(prv == l[k].fs && node == l[k].sc){
	    cur = k; dir = 0; break;
	  }
	  if(prv == l[k].sc && node == l[k].fs){
	    cur = k; dir = 1;  break;
	  }
	}
	if(g[cur][dir]>=0)break;
      }
      if(area>EPS){
	id.push_back(PolySet.size());
	PolySet.push_back(poly);
      }else{
	id.push_back(-1);
      }
    }
  }

  int ans = PolySet.size();
  init(PolySet.size());

  rep(i,PolySet.size()){
    rep(j,PolySet.size()){
      if(i==j)continue;
      bool f = true;
      rep(k,PolySet[i].size()){
	f &= in_poly(PolySet[j],PolySet[i][k]);
	if(!f)break;
      }
      if(f)ans -= unite(i,j);
    }
  }

  rep(i,l.size()){
    int a = id[g[i][0]], b = id[g[i][1]];
    if(a<0 || b<0)continue;
    ans -= unite(a,b);
  }

  cout << ans << endl;
}
accuracy: 1
time_ms: 70
memory_kb: 1584
final_rank: 6