<style> .reveal .slides { text-align: left; font-size:30px; } </style> # 進階計算幾何 ---- - Pick Theorem - Sweep Line - Area of Rectangles - Segment Intersection - Convex Hull trick - Circle cover - Half Plane Intersection --- ## Pick Theorem ### **A = i + b/2 − 1** - A: Area of polygon - i: Grid number in the inner - b: Grid number on the side ---- ### CSES --- Polygon Lattice Points Given a polygon, print two integers: - the number of lattice points **inside the polygon** - the number of lattice points **on its boundary**. - $n\le 10^5$ - $x, y\le 10^6$ --- ### Sweep line - Area of Rectangles - Segment Intersection ---- ## [矩形覆蓋面積](https://cses.fi/problemset/task/1741) ### Area of Rectangles 給定平面上 $n$ 個矩形,求總覆蓋面積。 ![](https://hackmd.io/_uploads/Bk55r-9h2.png) ---- $n\le 10$ ? <span>$\to$ 排容原理<br><!-- .element: class="fragment" data-fragment-index="1" --></span> <span>$\to$ 求出至少被一個矩形覆蓋的面積 - 至少被兩個 + 至少被三個 ...<br><!-- .element: class="fragment" data-fragment-index="2" --></span> ---- $n\le 10^5$ ? <span>$\to$ 掃描線 !<br><!-- .element: class="fragment" data-fragment-index="1" --></span> ---- 把每個矩形分成 start event 和 end event 掃描線從左往右掃,掃到矩形左界時加入計算面積,掃到右界時移除 ![](https://hackmd.io/_uploads/S13Y_b923.png =550x) ---- ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9zMi5heDF4LmNvbS8yMDE5LzA4LzA4L2VUdURqUC5naWY =650x) ---- 以三個矩形為例,分成六個事件 ![](https://hackmd.io/_uploads/B1e-O-zch2.png) 1. add $(5,5) - (5,15)$ 2. add $(10,10) - (10,20)$ 3. add $(10,20) - (10,25)$ 4. remove $(20,5) - (20,15)$ 5. remove $(20,20) - (20,25)$ 6. remove $(25,10) - (25,20)$ ---- 每次掃描線到新的事件時, 先計算前一個事件與當前事件的掃描區間覆蓋面積 ![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9zMi5heDF4LmNvbS8yMDE5LzA4LzA4L2VUdURqUC5naWY =650x) ---- ### add $(5,5) - (5,15)$ ![](https://hackmd.io/_uploads/B17QQM9h3.png) ---- ### add $(10,10) - (10,20)$ 到此事件時,加上前一個事件到當前事件的區間覆蓋面積。 area += 10 * (10-5) ![](https://hackmd.io/_uploads/BJSYXG5n3.png) ---- ### add $(10,20) - (10,25)$ area += 15 * (10-10) ![](https://hackmd.io/_uploads/HyvgNGqh2.png) ---- ### remove $(20,5) - (20,15)$ area += 20 * (20-10) ![](https://hackmd.io/_uploads/B1QD4f93h.png) ---- ### remove $(20,20) - (20,25)$ area += 15 * (20-20) ![](https://hackmd.io/_uploads/BJ8rSM92n.png) ---- ### remove $(25,10) - (25,20)$ area += 10 * (25-20) ![](https://hackmd.io/_uploads/H1KDHzcnn.png) ---- ### 如何維護區間覆蓋面積 此題的座標範圍為 $\pm 10^6$ 總共 2n 個事件,對於每個事件如何維護當前區間覆蓋面積 ? ---- 離散化 + 資料結構 ! ---- 把所有出現過的 y 座標離散化 最多為長度 2n 的序列 ![](https://hackmd.io/_uploads/SklZOGqn2.png) ---- 以 add $(10,10) - (10,20)$ 為例 等價於在時間軸 10 的時候,在區間 1-3(10-20) 加入一個矩形 ![](https://hackmd.io/_uploads/SklZOGqn2.png) ---- 線段樹對於每個區間需要維護兩個資訊 - 有多少個矩形覆蓋當前整個區間 - 當前區間覆蓋的總面積為何 ---- 對於每個事件,即可使用資料結構在 $O(\log n)$ 維護 總複雜度為 $O(n\log n)$ --- 給 $n$ $(1\le n\le 2\cdot 10^5)$ 個線段,求是否有任何相交? ---- 之前有教過如何判斷線段相交,窮舉 pair 複雜度為 $O(N^2)$ 但複雜度太高 $(1\le n\le 2\cdot 10^5)$ ---- ## 掃描線 把每條線段分成左右兩端點,從左往右依序加入/移除線段維護 ---- ## 如何維護線段 ? 可以發現如果兩條線段沒有相交, 則他們的上下關係在 在 x 軸重疊的部分不會改變 ![](https://hackmd.io/_uploads/SkujjM52h.png) 以線段 A 和 B 為例,A 和 B 在 x 軸重疊的部分,A 始終在 B 的下面 以線段 B 和 C 為例,B 和 C 在 x 軸重疊的部分,B 從下面跑到了上面 ---- 如果線段有相交,則在加入或移除時, 與相鄰的線段相交 ![](https://hackmd.io/_uploads/HJwqy79n2.png) 在事件 5 中,移除線段 C 時檢查是否與相鄰線段相交。 ---- 因此,每次在加入/移除線段時,判斷是否與當前相鄰的線段相交。 使用平衡二元樹可以在 $O(\log n)$ 快速找到相鄰的線段。 ---- ## 使用平衡二元樹維護 利用上下關係當成偏序關係維護線段,可以使用 set/treap 之類維護 使用 set 維護記得要寫 compare function ---- ### compare function 可以參考以下寫法: 使用線方程式帶入當前掃描線的 $x$ 座標去判斷 $y$ 大小 ```cpp= int nowx; // 掃描線當前 x 座標 double geth(Line p) { long long l = nowx - p.st.x, r = p.ed.x - nowx; return (r * p.st.y + l * p.ed.y) / (double)(l + r); } bool operator<(Line a, Line b) { return geth(a) < geth(b); } set<Line> s; ``` ---- ## 演算法 1. 把所有線段的起始點分成兩個加入/移除 兩個事件 2. 把所有事件照排序 x 軸排序 3. 從左到右掃描事件 - 如果為左端點,把此線段塞入平衡二元樹 - 如果為右端點,把此線段移除平衡二元樹 - 塞入/移除時,判斷是否與前/後一個線段相交 ---- 總複雜度 $O(n\log n)$ --- ### Convex hull trick [link](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/geometry/ConvexHulltrick.cpp) - $O(\log n)$ check point inside convex hull - $O(\log n)$ Find 2 tang pts on CH of a given outside point - $O(\log n)$ Find tangent points of a given vector - $O(\log n)$ Find intersection point of a given line ---- [link](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/geometry/ConvexHulltrick.cpp) - construct function - Conv(convex hull) ---- ## check point inside convex hull given point, return true if point inside convex hull. ```cpp bool contain(Pt p) ``` ---- ## NCPC 2021 --- E. Archery at the Summer Opymlic Camp 給定 $n$ 個凸包,每個凸包一層包一層, $q$ 筆詢問,每筆詢問給一個點,問在幾個凸包內 - $n, q\le 2\cdot 10^5$ ---- 模板可以 $O(\log n)$ 求出是否在某個凸包內,要求出包括幾個凸包可以直接二分搜。 ---- ## get tangent line of the point Given point $p$, return two point denote the tangent line from point $p$ to convex hull. ```cpp bool get_tang(Pt p, int &i0, int &i1) ``` ![](https://hackmd.io/_uploads/ryzvjZ822.png) ---- ## NCPC 2020 --- M. The Summit from Where I Stand 給一個上半凸包為一座山,$q$ 筆詢問每筆詢問求在位置 $(x, y)$ 時,同時可以看到幾個點 ![](https://hackmd.io/_uploads/rkovaPioh.png =450x) 帶入模板找出公切線 index 即可。 ---- ## Find tangent points of a given vector return the index of vertex has max cross value with vector ```cpp int get_tang(Pt vec) ``` ---- ## Find intersection point of a given line - return 1 and intersection is on edge (i, next(i)) - return 0 if no strictly intersection ```cpp bool get_intersection(Pt u, Pt v, int &i0, int &i1) ``` ![](https://hackmd.io/_uploads/HJxz6Z822.png) --- ### Circle cover 給定 C 個圓,回傳一個陣列 Area, 其中 Area[i] 為至少包括 i 個圓的覆蓋面積 [link](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/geometry/CircleCover.cpp) - init(int _c) - 總共 _c 個圓 - Circ c[ N ] - 填入圓的圓心&半徑 - solve() - 跑 CircleCover $O(n^2\log n)$ --- ### 半平面交 ### Half Plane Intersection ---- ### 半平面 每條直線能把平面切成兩半。 以方程式 $ax+by+c\le0$ 可以代表一個半平面。 ![](https://hackmd.io/_uploads/Hk6Z-z8h3.png =300x) $5x + 3y \le 5$ ---- ### 半平面交 半平面交為多個半平面的交集區域,通常為一個凸多邊形 ![](https://hackmd.io/_uploads/H1M-fGU2h.png =300x) - $5x + 3y - 5\le 0$ - $2 x-3 y + 7\le 0$ ---- 以下三個半平面圍成一個凸多邊形 ![](https://hackmd.io/_uploads/rJZjQM8n2.png =300x) - $5x + 3y - 5\le 0$ - $2 x-3 y + 7\le 0$ - $-2 x+0 y - 15\le 0$ ---- ## 模板 [[line define]](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/geometry/definition.cpp) [[link]](https://github.com/jakao0907/CompetitiveProgrammingCodebook/blob/master/geometry/Half_plane_intersection.cpp) ```cpp vector<Pt> HPI(vector<Line>& L) ``` 傳入每條方程式的兩點方程式 (半平面為點 $st$ 往 $ed$ 的逆時針方向) 回傳值為形成的凸多邊形的頂點 vector $O(n\log n)$ --- ## NCPC 2021 --- Overlapping Triangles $Q$ 筆詢問,每筆詢問給定兩個三角形,求交集面積 (面積以分數表示) - $Q\le 80$ - $|x|, |y|\le 10^{18}$ ![](https://hackmd.io/_uploads/rkKxhzL2h.png =x350) ---- 分 case 找交點 ? ---- 轉換為半平面就做完了 XD ![](https://hackmd.io/_uploads/rkMa6fL23.png =x350) --- [HW](https://vjudge.net/contest/575062)
{"title":"進階計算幾何","description":"掃描線","contributors":"[{\"id\":\"19f09ccf-6b99-452f-971f-955cfc1657f3\",\"add\":7984,\"del\":951}]"}
    688 views
   Owned this note