1. 程式人生 > >HDU 1542 Atlantis (線段樹 + 掃描線 + 離散化)

HDU 1542 Atlantis (線段樹 + 掃描線 + 離散化)

Atlantis

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 8998    Accepted Submission(s): 3856


Problem Description There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.

Input The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.

The input file is terminated by a line containing a single 0. Don’t process it.
Output For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.

Output a blank line after each test case.

Sample Input 2 10 10 20 20 15 15 25 25.5 0
Sample Output Test case #1 Total explored area: 180.00 所謂的離散化,大家可以簡單的理解為,將一組很大的資料,濃縮為一組很小的資料,用這組資料來代替原資料的作用,
比如給你1000個數,數的範圍為(1,1e18)我們這裡就可以用離散化,由於只有1000個數,我們可以用一個數組的下標代表提供的每一數,如果需要這個資料了,由於是下標,可以直接通過下標獲得,如此就是離散化。講得很基礎,是個很不錯的部落格然後提醒一下這個掃描線要注意的問題就是區間的問題一般的線段樹以及我們的區間修改合併,都有一個共同點,就是不會出現區間缺失的現象,什麼叫區間缺失,顧名思義,區間缺失就是缺少一些區間沒有進行運算,這裡的掃描線就會遇到這個問題。普遍的,我們的線段樹以及資料區間分佈是這樣的:[1, a][a + 1, b][b + 1, c][c + 1, d][d + 1, e].......但是如果只是簡簡單單的用這個來解決掃描線的問題會導致錯誤,為什麼因為,他沒有涉及到[a,a + 1],在掃描線中會出現[a,a + 1]中的資料,而常用的線段樹的區間概念是無法解決這樣的問題的,出現了所謂的區間缺失,怎樣解決,下面的程式碼給出瞭解決方案,這裡簡單的提一下,就是利用[ , ),這個區間性質,左閉右開,即可解決區間缺失問題
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
typedef long long LL;
#define lson rt << 1, l, mid
#define rson rt << 1|1, mid + 1, r
const int MAXN = 2000 + 5;
int  Col[MAXN << 2], n, cnt, res;
double X[MAXN << 2], Sum[MAXN << 2];
struct seg {
    double l,r,h;
    int s;
    seg() {}
    seg(double l,double r,double h,int s):l(l),r(r),h(h),s(s) {}
    bool operator < (const seg & object) const {
        return h < object.h;
    }
} S[MAXN];


void pushup(int rt,int l,int r) {
    if (Col[rt]) Sum[rt] = X[r+1] - X[l];//利用[ , ),這個區間性質,左閉右開
    else if (l == r) Sum[rt] = 0;
    else Sum[rt] = Sum[rt<<1] + Sum[rt<<1|1];
}

void update(int L, int R, int c,int rt,int l, int r) {
    if(L <= l && r <= R) {
        Col[rt] += c;
        pushup(rt,l,r);
        return ;
    }
    int mid = (l + r) >> 1;
    if(L <= mid) update(L, R, c, lson);
    if(R > mid) update(L, R, c, rson);
    pushup(rt,l,r);
}

int binary_find(double x){
    int lb = -1,ub = res - 1;
    while(ub - lb > 1){
        int mid = (lb + ub) >> 1;
        if(X[mid] >= x) ub = mid;
        else lb = mid;
    }
    return ub;
}

int main() {
    int cas = 1;
    while(~ scanf("%d", &n), n) {
        cnt = res = 0;
        for(int i = 0 ; i < n; i ++) {
            double a,b,c,d;
            scanf("%lf%lf%lf%lf",&a, &b, &c,&d);
            S[cnt] = seg(a, c, b, 1);
            X[cnt ++] = a;
            S[cnt] = seg(a, c, d, -1);
            X[cnt ++] = c;
        }
        sort(X, X + cnt);
        sort(S, S + cnt);
        res ++;
        for(int i = 1; i < cnt; i ++) {
            if(X[i] != X[i - 1]) X[res ++] = X[i];
        }

        memset(Sum, 0, sizeof(Sum));
        memset(Col, 0, sizeof(Col));
        double ans = 0;
        for(int i = 0;i < cnt - 1;i ++){
            int l = binary_find(S[i].l);
            int r = binary_find(S[i].r) - 1;//利用[ , ),這個區間性質,左閉右開
            update(l, r, S[i].s, 1, 0, res - 1);
            ans += Sum[1] * (S[i + 1].h - S[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",cas++ , ans);
    }
    return 0;
}