亞特蘭蒂斯
題目描述
有幾個古希臘書籍中包含了對傳說中的亞特蘭蒂斯島的描述。
其中一些甚至包括島嶼部分地圖。
但不幸的是,這些地圖描述了亞特蘭蒂斯的不同區域。
您的朋友 Bill 必須知道地圖的總面積。
你自告奮勇寫了一個計算這個總面積的程式。
輸入格式
輸入包含多組測試用例。
對於每組測試用例,第一行包含整數 n,表示總的地圖數量。
接下來 n 行,描繪了每張地圖,每行包含四個數字 x1,y1,x2,y2(不一定是整數),(x1,y1) 和 (x2,y2) 分別是地圖的左上角位置和右下角位置。
注意,座標軸 x 軸從上向下延伸,y 軸從左向右延伸。
當輸入用例 n=0 時,表示輸入終止,該用例無需處理。
輸出格式
每組測試用例輸出兩行。
第一行輸出 Test case #k,其中 k 是測試用例的編號,從 1 開始。
第二行輸出 Total explored area: a,其中 a 是總地圖面積(即此測試用例中所有矩形的面積並,注意如果一片區域被多個地圖包含,則在計算總面積時只計算一次),精確到小數點後兩位數。
在每個測試用例後輸出一個空行。
資料範圍
\(1≤n≤10000,0≤x1<x2≤100000,0≤y1<y2≤100000\)
注意,本題 n 的範圍上限加強至 10000。
輸入樣例:
2
10 10 20 20
15 15 25 25.5
0
輸出樣例:
Test case #1 Total explored area: 180.00
思路
假設給的影象是這樣。
實際要求的面積是這樣
我們先來想暴力的思路。假設每個座標都是整數,那就是一行一行的數有多少個格子被塗上了顏色。
這樣顯然會TLE,但是我們的思路不變,想想怎麼才能優化這個數格子的步驟。
首先我們想到每個豎線到下一個豎線前它的len值肯定是不變的,那麼我們就不需要一行一行的數了,只需要在對於每一道豎線那數一下,然後乘上豎線之間的差。
那麼接下來的問題是怎麼能快速算出每一個豎線所對應的長度。由於在每個x那都可能修改還有查詢,這樣大量的區間查詢與修改操作,我們難免會想到線段樹。
那麼我們就把初始每個矩形的左邊標記為+1表示矩形開始,右邊標記為-1表示這個矩形結束。被標記為正數的區域即線段長度。
這題所採用的x,y都是double型別的需要我們進行離散化。
接下來面臨的問題是怎麼去維護這個線段樹。
我們不需要查詢函式,因為每次的查詢都是對於整個區間查詢長度,即\(tr[1].len\)
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e6 + 10;
struct Seg{
double x, y1, y2;
int k;
bool operator < (const Seg& t) const {
return x < t.x;
}
}s[N];
struct Node{
int l, r;
int cnt;
double len;
}tr[N];
vector<double> all;
inline int find(double y){
return lower_bound(all.begin(), all.end(), y) - all.begin();
}
void pushup(int u){
if(tr[u].cnt)
tr[u].len = all[tr[u].r + 1] - all[tr[u].l];
else if(tr[u].l != tr[u].r)
tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
else tr[u].len = 0;
}
void build(int u, int l, int r){
tr[u] = { l, r, 0, 0 };
if(l != r){
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
}
void modify(int u, int l, int r, int d){
if(tr[u].l >= l && tr[u].r <= r)tr[u].cnt += d;
else {
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)modify(u << 1, l, r, d);
if(r > mid)modify(u << 1 | 1, l, r, d);
}
pushup(u);
}
int main(){
int n, t = 1;
while(scanf("%d", &n), n){
all.clear();
for(int i = 0, j = 0; i < n; i++){
double x1, x2, y1, y2;
scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
s[j++] = { x1, y1, y2, 1 };
s[j++] = { x2, y1, y2, -1 };
all.push_back(y1), all.push_back(y2);
}
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
build(1, 0, all.size() - 2);
sort(s, s + n * 2);
double res = 0;
for(int i = 0; i < n * 2; i++){
if(i)res += tr[1].len * (s[i].x - s[i - 1].x);
modify(1, find(s[i].y1), find(s[i].y2) - 1, s[i].k);
}
printf("Test case #%d\nTotal explored area: %.2lf\n\n", t++, res);
}
return 0;
}