1. 程式人生 > >題解 POJ1151【Atlantis】

題解 POJ1151【Atlantis】

題目大意

給你n個矩形,讓你求所有矩形覆蓋的總面積,重疊的地方只算一次

輸入格式

輸入的第一行包含一個整數n,表示可得到的地圖數目。

以下n行,每行描述一張地圖。每行包含4個整數x1,y1,x2和y2(0≤x1<x2≤30000,0≤y1<y2≤30000)。數值(x1,y1)和(x2,y2)是座標,分別表示繪製區域的左下角和右上角座標。每張地圖是矩形的,並且它的邊是平行於x座標軸或y座標軸的。

資料可能有多組,當n為零時停止輸入

輸出格式

對於每個測試資料,你的程式應該輸出一個答案。每個答案的第一行必須是“Test case #k”,其中k是測試資料的編號(從1開始)。第二個必須是“Total explored area: a”,其中a是總探索麵積(即在本測試案例中所有矩形的並集面積),精確到小數點右邊兩位。
在每個測試用例後輸出一個空行。

樣例輸入

2
10 10 20 20
15 15 25 25.5
0

樣例輸出

 Test case #1
 Total explored area: 180.00 
 

資料範圍

 n <= 100

主要思路:線段樹 + 掃描線

這是一道掃描線裸題。

我們把要覆蓋的矩形抽象成兩個線,這兩條線就被稱為掃描線。如圖(網上扒拉來的圖QwQ,若有侵權請聯絡博主刪除QwQ)

對於掃描線,我們可以把掃描線的高度進行離散化。然後我們把x軸上的線(圖中的黑線)分隔開的就可以抽象成幾段線段。

我們只需要把這些交點用線段樹維護就好。然後我們把所有掃描線從最小到最大排序一下。我們首先記錄一條掃描線的左端點與右端點,它的高度,它是下底邊還是上界邊,如果是下底邊就記為1,上界邊記為-1。(這裡實際是做了個差分,就是維護兩條掃描線穿過(或切於邊界的)矩形有多少。

線段樹中維護的是這段區間的下底邊個數和下底邊總長度。我們掃描的作是每次更新下底邊總長度和下底邊個數增加新面積。我們只要從最下向上掃描一遍統計總面積就好了。

code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <queue>
#include <stack>
#include <map>
#include <vector>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
#define inf 1 << 30
#define mn 100010
#define ll long long
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); }
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
struct tree{
    int mark; double sum;
} z[mn << 2];
struct seg{
    double l, r, h;
    int d;
    seg() {}
    seg(double _l, double _r, double _h, int _d) : l(_l), r(_r), h(_h), d(_d) {}
    bool operator < (const seg &b) const { return h < b.h; }
} s[mn];
int n, num, kkk;
double ha[mn];
double x, y, xx, yy;
#define root 0, m - 1, 1
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define bson l, r, rt
inline void update(int l, int r, int rt) {
    if(z[rt].mark) z[rt].sum = ha[r + 1] - ha[l];
    else if(l == r) z[rt].sum = 0;
    else z[rt].sum = z[rt << 1].sum + z[rt << 1 | 1].sum;
}
inline void modify(int l, int r, int rt, int nowl, int nowr, int d) {
    if(nowl <= l && r <= nowr) {
        z[rt].mark += d;
        update(bson);
        return;
    }
    int m = (l + r) >> 1;
    if(nowl <= m) modify(lson, nowl, nowr, d);
    if(m < nowr)  modify(rson, nowl, nowr, d);
    update(bson);
} 
inline int search(double key, double* x, int n) {
    int l = 0, r = n - 1;
    while(l <= r) {
        int m = (l + r) >> 1;
        if(x[m] == key) return m;
        if(x[m] > key) r = m - 1;
        else l = m + 1; 
    }
    return -1;
}
int main() {
    while(cin >> n, n) {
        num = 0;
        go(i, 0, n - 1, 1) {
            cin >> x >> y >> xx >> yy;
            ha[num] = x;
            s[num++] = seg(x, xx, y, 1);
            ha[num] = xx;
            s[num++] = seg(x, xx, yy, -1);
        }
        sort(ha, ha + num);
        sort(s, s + num);
        int m = 1;
        go(i, 1, num - 1, 1) 
            if(ha[i] != ha[i - 1]) ha[m++] = ha[i];
        double ans = 0;
        go(i, 0, num - 1, 1) {
            int L = search(s[i].l, ha, m);
            int R = search(s[i].r, ha, m) - 1;
            modify(root, L, R, s[i].d);
            ans += z[1].sum * (s[i + 1].h - s[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n", ++kkk, ans);
    }
    return 0;
}