1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
| // 더 많은 정보는 42jerrykim.github.io 에서 확인하세요.
#include <bits/stdc++.h>
using namespace std;
using int64 = long long;
struct Point { int64 x, y; };
static inline __int128 sq(__int128 v){ return v*v; }
static inline __int128 dist2(const Point& p){ return sq((__int128)p.x) + sq((__int128)p.y); }
// ccw of (b - a) x (c - a)
static inline int ccw(const Point& a, const Point& b, const Point& c){
__int128 vx1 = (__int128)b.x - a.x;
__int128 vy1 = (__int128)b.y - a.y;
__int128 vx2 = (__int128)c.x - a.x;
__int128 vy2 = (__int128)c.y - a.y;
__int128 t = vx1 * vy2 - vy1 * vx2;
if (t > 0) return 1;
if (t < 0) return -1;
return 0;
}
// 각도 비교: +x 반평면 우선, 같은 반평면이면 원점 기준 각도 오름차순
static inline bool angle_cmp(const Point& a, const Point& b){
bool ah = (a.x > 0) || (a.x == 0 && a.y > 0);
bool bh = (b.x > 0) || (b.x == 0 && b.y > 0);
if (ah ^ bh) return ah > bh;
int t = ccw({0,0}, a, b);
if (t != 0) return t > 0;
// 동각은 없다고 가정되지만 안전하게 거리로 분기
return dist2(a) < dist2(b);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<pair<Point,int>> ev; ev.reserve(2*n);
vector<Point> stp(n+1), enp(n+1);
vector<int> si(n+1), ei(n+1);
for (int i = 1; i <= n; ++i){
int m; cin >> m;
vector<Point> v(m);
for (int j = 0; j < m; ++j) cin >> v[j].x >> v[j].y; // y >= 1
// p1 = 각도 최소, p2 = 각도 최대
Point p1 = *max_element(v.begin(), v.end(), angle_cmp); // 뒤집어 사용할 것이므로 max/min 반대로 선정
Point p2 = *min_element(v.begin(), v.end(), angle_cmp);
stp[i] = p1; enp[i] = p2;
ev.emplace_back(p1, +i);
ev.emplace_back(p2, -i);
}
// 각도 정렬 후 reverse (angle_cmp가 +x -> -x 순서를 만들므로 뒤집어 사용)
sort(ev.begin(), ev.end(), [&](const auto& A, const auto& B){ return angle_cmp(A.first, B.first); });
reverse(ev.begin(), ev.end());
for (int i = 0; i < (int)ev.size(); ++i){
int id = ev[i].second;
if (id > 0) si[id] = i; else ei[-id] = i;
}
struct Node { int id; };
struct SegLess {
const vector<int>& si; const vector<int>& ei; const vector<Point>& stp; const vector<Point>& enp;
bool operator()(const Node& A, const Node& B) const {
int a = A.id, b = B.id; if (a == b) return false;
if (si[a] <= si[b] && ei[b] <= ei[a]) return ccw(stp[a], enp[a], stp[b]) > 0; // a가 b 포함
if (si[b] <= si[a] && ei[a] <= ei[b]) return ccw(stp[b], enp[b], stp[a]) < 0; // b가 a 포함
if (si[a] < si[b]) return ccw(stp[a], enp[a], stp[b]) > 0; // 엇갈림
return ccw(stp[b], enp[b], stp[a]) < 0;
}
} segLess{si, ei, stp, enp};
set<Node, SegLess> S(segLess);
vector<char> seen(n+1, 0);
for (int i = 0; i < (int)ev.size(); ++i){
int id = ev[i].second;
if (id > 0){
S.insert({id});
}else{
id = -id;
auto it = S.find({id});
if (it != S.end()) S.erase(it);
}
if (!S.empty()) seen[S.begin()->id] = 1; // 현재 각도에서 보이는 선분
}
int vis = 0; for (int i = 1; i <= n; ++i) vis += seen[i];
cout << (n - vis) << '\n';
return 0;
}
|