掃 貓 線(誤)
阿新 • • 發佈:2021-11-13
\[\tt\large\color{cornflowerblue}{Scanningline studying note: NO.1.}
\]
這下面的內容是蒯
的OI-WIKI上的,而OI-WIKI是蒯
的部落格上的(
下面是原文的版權宣告.
- 版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。
原文地址: link
-
簡介
掃描線一般運用在圖形上面,它和它的字面意思十分相似,就是一條線在整個圖上掃來掃去,它一般被用來解決圖形面積,周長等問題。
-
Atlantis 問題
在二維座標系上,給出多個矩形的左下以及右上座標,求出所有矩形構成的圖形的面積。
\[\huge\tt\color{gold}{>>姐法/Solution} \]現在假設我們有一根線,從下往上開始掃描:
- 如圖所示,我們可以把整個矩形分成如圖各個顏色不同的小矩形,那麼這個小矩形的高就是我們掃過的距離,那麼剩下了一個變數,那就是矩形的長一直在變化。
- 我們的線段樹就是為了維護矩形的長,我們給每一個矩形的上下邊進行標記,下面的邊標記為 1,上面的邊標記為 -1,每遇到一個矩形時,我們知道了標記為 1 的邊,我們就加進來這一條矩形的長,等到掃描到 -1 時,證明這一條邊需要刪除,就刪去,利用 1 和 -1 可以輕鬆的到這種狀態。
- 還要注意這裡的線段樹指的並不是線段的一個端點,而指的是一個區間,所以我們要計算的是 \(r+1\) 和 \(r-1\) 。
- 需要
離散化
。
code on OI-WIKI:
#include <algorithm> #include <cstdio> #include <cstring> #define maxn 300 using namespace std; int lazy[maxn << 3]; // 標記了這條線段出現的次數 double s[maxn << 3]; struct node1 { double l, r; double sum; } cl[maxn << 3]; // 線段樹 struct node2 { double x, y1, y2; int flag; } p[maxn << 3]; // 座標 //定義sort比較 bool cmp(node2 a, node2 b) { return a.x < b.x; } //上傳 void pushup(int rt) { if (lazy[rt] > 0) cl[rt].sum = cl[rt].r - cl[rt].l; else cl[rt].sum = cl[rt * 2].sum + cl[rt * 2 + 1].sum; } //建樹 void build(int rt, int l, int r) { if (r - l > 1) { cl[rt].l = s[l]; cl[rt].r = s[r]; build(rt * 2, l, (l + r) / 2); build(rt * 2 + 1, (l + r) / 2, r); pushup(rt); } else { cl[rt].l = s[l]; cl[rt].r = s[r]; cl[rt].sum = 0; } return; } //更新 void update(int rt, double y1, double y2, int flag) { if (cl[rt].l == y1 && cl[rt].r == y2) { lazy[rt] += flag; pushup(rt); return; } else { if (cl[rt * 2].r > y1) update(rt * 2, y1, min(cl[rt * 2].r, y2), flag); if (cl[rt * 2 + 1].l < y2) update(rt * 2 + 1, max(cl[rt * 2 + 1].l, y1), y2, flag); pushup(rt); } } int main() { int temp = 1, n; double x1, y1, x2, y2, ans; while (scanf("%d", &n) && n) { ans = 0; for (int i = 0; i < n; i++) { scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2); p[i].x = x1; p[i].y1 = y1; p[i].y2 = y2; p[i].flag = 1; p[i + n].x = x2; p[i + n].y1 = y1; p[i + n].y2 = y2; p[i + n].flag = -1; s[i + 1] = y1; s[i + n + 1] = y2; } sort(s + 1, s + (2 * n + 1)); // 離散化 sort(p, p + 2 * n, cmp); // 把矩形的邊的橫座標從小到大排序 build(1, 1, 2 * n); // 建樹 memset(lazy, 0, sizeof(lazy)); update(1, p[0].y1, p[0].y2, p[0].flag); for (int i = 1; i < 2 * n; i++) { ans += (p[i].x - p[i - 1].x) * cl[1].sum; update(1, p[i].y1, p[i].y2, p[i].flag); } printf("Test case #%d\nTotal explored area: %.2lf\n\n", temp++, ans); } return 0; }