HDU1542 Atlantis(线段树+扫描线)

Atlantis

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


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
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
 

Source
Mid-Central European Regional Contest 2000
 

Recommend
linle
题意:给出n个矩形的左下角和右上角坐标,求他们所覆盖的面积。

 

mark[rt]表示区间内上边和底边的相差个数,hash数组存储经过离散化并且按升序排序的竖边, 用sum[1]表示当前的底边长度。

线段树+扫描线求覆盖面积:

把矩形的竖边(正着看)也就是按x值离散化并按升序排序,横边按y值升序排序,按顺序从最底下的底边开始更新线段树(更新当前底边),如果是底边则把相应区间内的mark-1,如果是上边则相应区间的mark+1,当mark为正数时表示相应区间可以完全当作底边,利用线段树更具mark的值更新sum,每次插入一条底边就可以求得对应的一部分面积,最后的和便是整体覆盖面积。

插入竖边按照横边扫也可以。

 

HDU1542 Atlantis(线段树+扫描线)_第1张图片

往线段树插入边1

 HDU1542 Atlantis(线段树+扫描线)_第2张图片

红色表示当前底边,绿色表示当前可求面积= sum[1]*(egde[2].h-edge[1].h)

 

插入边2

 HDU1542 Atlantis(线段树+扫描线)_第3张图片

由于插入的底边,所有底边长度不会减少,当前可求面积=sum[1]*(egde[3].h-edge[2].h)

 

插入边3

 HDU1542 Atlantis(线段树+扫描线)_第4张图片

当前底边长度增加,当前可求面积=sum[1]*(egde[4].h-edge[3].h)

 

插入边4

 HDU1542 Atlantis(线段树+扫描线)_第5张图片

由于插入了上边,底边长度变化,也就是通过更新区间改变了sum[1](当前底边长度),当前可求面积=sum[1]*(egde[5].h-edge[4].h)

mark是不会出现负数的,因为上边和底边是对应的,当一条上边出现时,它前面必定至少出现过一条底边。

#include 
#include 
#include 
using namespace std;
#define maxn 210
double Hash[maxn], sum[maxn<<2];
int mark[maxn<<2];
struct Edge{
    double l, r, h;
    int c;
    Edge(){}
    Edge(double x1, double x2, double H, int C):l(x1),r(x2),h(H),c(C){}
    bool operator<(const Edge &a)const{
        return h>1;
        if(a[mid] == key) return mid;
        if(a[mid] < key) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
void upfather(int n, int left, int right){
    if(mark[n]) sum[n] = Hash[right+1] - Hash[left];
    else if(left == right) sum[n] = 0;//叶子结点长度为0
    else sum[n] = sum[n<<1] + sum[n<<1|1];
}
void update(int L, int R, int rt, int d, int left, int right){
    if(L<=left&&right<=R){
        mark[rt] += d;
        upfather(rt, left, right);
        return;
    }
    int mid = (left+right)>>1;
    if(L<=mid) update(L, R, rt<<1, d, left, mid);
    if(R>mid) update(L, R, rt<<1|1, d, mid+1, right);
    upfather(rt, left, right);
}
int main()
{
    int n, i, j, k, m, T = 1;
    double x1, x2, y1, y2;
    while(~scanf("%d", &n)){
        //不用初始化sum和mark,因为扫描完最后一条边后, mark和sum都会变为0
        if(!n) continue;
        k = 0;
        for(i = 0;i < n;i++){
            scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
            Hash[k] = x1;
            edge[k++] = Edge(x1, x2, y1, 1);
            Hash[k] = x2;
            edge[k++] = Edge(x1, x2, y2, -1);
        }
        sort(Hash, Hash+k);
        sort(edge, edge+k);
        m = 1;
        for(i = 1;i < k;i++)
            if(Hash[i] != Hash[i-1])
                Hash[m++] = Hash[i];
        double ans = 0;
        for(i = 0;i < k;i++){
            int L = Search(edge[i].l, Hash, m);
            int R = Search(edge[i].r, Hash, m)-1;
            update(L, R, 1, edge[i].c, 0, m-1);
            ans += sum[1]*(edge[i+1].h-edge[i].h);
            printf("%lf\n", sum[1]);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n", T++, ans);
    }
}
/*
引用自他人:
这里注意下扫描线段时r-1:int R=search(s[i].l,hash,m)-1;
计算底边长时r+1:if(mark[n])sum[n]=hash[right+1]-hash[left];
解释:假设现在有一个线段左端点是l=0,右端点是r=m-1
则我们去更新的时候,会算到sum[1]=hash[mid]-hash[left]+hash[right]-hash[mid+1]
这样的到的底边长sum是错误的,why?因为少算了mid~mid+1的距离,由于我们这利用了
离散化且区间表示线段,所以mid~mid+1之间是有长度的,比如hash[3]=1.2,hash[4]=5.6,mid=3
所以这里用r-1,r+1就很好理解了
*/

 
   

 

你可能感兴趣的:(HDU,线段树)