POJ 1151 Atlantis 线段树面积并

来源:互联网 发布:光环大数据和达内 编辑:程序博客网 时间:2024/05/29 08:25

Atlantis
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 19878 Accepted: 7523
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 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

题意,给n个矩形的左上角坐标和右下角坐标,求出这些矩形面积合并起来总共的面积。

画出图,从最底下一根线网上扫描,计算下底边合并起来的总长度乘以这一横边到上一横边之间的高,扫描到最定边的下面一条边就行了。

坐标的记录,因为都是浮点数,所以进行离散化,过程就是用一个数组记录下来,排序,去重,需要时二分查找点就行了。

对len[]进行更新,len[id]对应区间[l,r],表示在[l,r]区间内,有多少线段是被覆盖了,这些被覆盖的区间的长度用来更新到根节点len[1],len[1]表示在总区间内多少线段被覆盖,然后用于乘高度算面积。

我用x[]来离散化点,这里的x[]中的每个点应该理解为这个点对应到下一个点的总长度,对len[]更新的是更新的区间被覆盖长度,所以进行更新的时候r要减1。想成是长度,再去想更新,更容易理解。

既然成段更新,为什么不Push_Down()?因为下边与上边总是成对的加减,标记会自动变化,遇到上边标记-1即抵消下边的值1,因为各边是叠加状态,只要不为0,都会算在面积并里面。

#include"stdio.h"#include"iostream"#include"algorithm"#include"string.h"#include"queue"#include"math.h"typedef long long LL;const int maxn = 2000+10;using namespace std;struct node{    double l,r,h;     ///矩形横边左右坐标,上边或下边高度h    int flag;        ///标记上下边,上边下边任意边为-1都不影响}poi[maxn*4];double len[maxn*4];///区间长度double x[maxn*4]; ///离散化用int mark[maxn*4];///标记void add(double x1,double x2,double h,double flag,int cnt) ///存入边{    poi[cnt].l = x1;    poi[cnt].r = x2;    poi[cnt].h = h;    poi[cnt].flag = flag;}bool cmp(node a,node b){    return a.h < b.h;}int Find(double val,int lon)  ///二分查找{    int l = 0;    int r = lon;    while(l <= r)    {        int mid = (l+r)/2;        if(x[mid] == val)            return mid;        else if(val < x[mid])            r = mid-1;        else            l = mid+1;    }}void Push_Up(int l,int r,int id)         ///向上更新{    if(mark[id]) len[id] = x[r+1]-x[l];  ///有标记就计算长度    else if(l == r) len[id] = 0;         ///对于结点,标记为0,不能进行覆盖,不算在面积长度里面    else len[id] = len[id*2]+len[id*2+1];///对于其他区间,子节点标记不一定为0,进行更新操作,手动模拟可懂}void update(int L,int R,int c,int l,int r,int id){    if(L <= l && r <= R)    {        mark[id] += c;        Push_Up(l,r,id);  ///不确定是否被标记,是否是结点,所以更新        return;    }    int mid = (l+r)/2;    if(L <= mid) update(L,R,c,l,mid,id*2);    if(mid < R)  update(L,R,c,mid+1,r,id*2+1);    Push_Up(l,r,id);}int main(void){    int n,cas = 1;    while(scanf("%d",&n) && n)    {        int cnt = 0;        for(int i = 1;i <= n;i++)        {            double x1,y1,x2,y2;            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);            x[cnt] = x1;            add(x1,x2,y1,-1,cnt);            cnt++;            x[cnt] = x2;            add(x1,x2,y2,1,cnt);            cnt++;        }        sort(poi,poi+cnt,cmp);        sort(x,x+cnt);        int lon = 1;        for(int i = 1;i < cnt;i++)    ///离散化        {            if(x[i] != x[i-1])                x[lon++] = x[i];        }        memset(len,0,sizeof len);        memset(mark,0,sizeof mark);        double ret = 0;        for(int i = 0;i < cnt-1;i++)        {            int L = Find(poi[i].l,lon);            int R = Find(poi[i].r,lon) - 1;   ///每个点对应一个线段,所以-1            update(L,R,poi[i].flag,0,lon-1,1);            ret += len[1]*(poi[i+1].h-poi[i].h);        }        printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ret);    }    return 0;}
0 0