AcWing 247. 亞特蘭蒂斯
阿新 • • 發佈:2022-04-19
本題特殊一點就是支援小數,需要離散化,其它的和粉刷油漆那題一樣的,不多說了:
#include <bits/stdc++.h> using namespace std; const int N = 100010; struct Segment { double x, y1, y2; int k; bool operator<(const Segment &t) const { return x < t.x; } } seg[N * 2]; struct Node { int l, r; int cnt; double len; } tr[N * 8]; //由於線段二倍,所以8倍空間 vector<double> ys; //用於離散化 //離散化+二分 int find(double y) { // 需要返回vector 中第一個 >= y 的數的下標 return lower_bound(ys.begin(), ys.end(), y) - ys.begin(); } void pushup(int u) { if (tr[u].cnt) tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l]; else if (tr[u].l == tr[u].r) tr[u].len = 0; else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len; } void build(int u, int l, int r) { tr[u] = {l, r, 0, 0}; if (l == r) return; int mid = l + r >> 1; build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r); // 因為後面兩個都是0, 所以不需要push_up; } void modify(int u, int l, int r, int k) { if (tr[u].l >= l && tr[u].r <= r) { tr[u].cnt += k; pushup(u); // 掃描線 特殊在 修改[l, r]區間時,只需要修改父節點,不需要push_down操作來更新兒子節點 // 因為只要父節點的tag > 0 永遠沒兒子節點什麼事 // 如果父節點的tag == 0, 那麼把父節點push_up一下就行, 查詢的時候只要輸出父節點的長度就行, 同樣也沒兒子節點什麼事 // 所以即便是修改區間, 也不需要lazt_tag } else { int mid = tr[u].l + tr[u].r >> 1; if (l <= mid) modify(u << 1, l, r, k); if (r > mid) modify(u << 1 | 1, l, r, k); pushup(u); } } int main() { int n, T = 1; while (cin >> n, n) { ys.clear(); int idx = 0; for (int i = 0; i < n; i++) { double x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; seg[idx++] = {x1, y1, y2, 1}; seg[idx++] = {x2, y1, y2, -1}; ys.push_back(y1), ys.push_back(y2); } //離散化,之所以需要離散化,是因為y可能是小數,小數沒法使用線段樹,需要有對映的關係整數出現 sort(ys.begin(), ys.end()); ys.erase(unique(ys.begin(), ys.end()), ys.end()); //例子:假設現在有三個不同的y軸點,分為兩個線段 // y[0] ~ y[1],y[1] ~ y[2]; //此時ys.size()為3,ys.size() - 2 為 1; //此時為 build(1, 0, 1); //有兩個點0 和 1,線段樹中0號點為y[0] ~ y[1],1號點為y[1] ~ y[2]; build(1, 0, ys.size() - 2); //實踐證明,多開2個也沒啥問題 //線段按x排序 sort(seg, seg + idx); double res = 0; for (int i = 0; i < n * 2; i++) { //第一條豎邊無法計算面積 if (i > 0) res += tr[1].len * (seg[i].x - seg[i - 1].x); //根結點的len * 陰影部分高度 modify(1, find(seg[i].y1), find(seg[i].y2) - 1, seg[i].k); //維護的是一小段區間 } printf("Test case #%d\n", T++); printf("Total explored area: %.2lf\n\n", res); } return 0; }