hdu 1542 Atlantis (线段树之扫描线)

http://acm.hdu.edu.cn/showproblem.php?pid=1542
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
题目大意:给你几个矩形的左下角和右上角,求出这几个矩形的面积,重复部分只算一次。
解题思路:很明显这个一个求相加矩形面积的题,需要用到扫描线和线段树结合。下面我们就来说一下扫描线。
首先明白一个定义,我们将矩形的左右两条边分别叫做左边界(即与y轴平行x坐标较小的那个边)和右边界(即与y轴平行x坐标较大的那个边)如果是左边界cover要+=1,右边界cover-+1,(cover的用途后边再具体解释)然后我们按照x坐标从小到大排序就可以得到我们需要的扫描线。如下图所示:
hdu 1542 Atlantis (线段树之扫描线)_第1张图片
图中4条红线从左到右分别是扫描线1,扫描线2,扫描线3,扫描线4。扫描线分别保存这几个变量 x 扫描线的横坐标 y_up 扫描线的上线 y_down 扫描线的下限 flag 这条扫描线是左边界还是右边界,左边界为-1右边界为1
然后我们再将y坐标离散化来构建线段树维护我们要求的面积。
hdu 1542 Atlantis (线段树之扫描线)_第2张图片
其中线段树中的值有x用来表示当前插入扫描线的上一条扫描线的横坐标y_up 线段树的左区间y_down线段树的右区间 flag表示这个节点是否是叶子节点 cover表示线段树的区间有没有被覆盖,若cover>0表示被覆盖。
当我们插入第一条扫描线时,由于cover值为0,所以没有计算任何面积
插入第二条扫描线计算面积为图中阴影部分。因为当插入第二条扫描边时只有【10,15】部分的cover值大于0
hdu 1542 Atlantis (线段树之扫描线)_第3张图片
插入第三条扫描线时计算面积为下图阴影部分,因为插入第三条时【10,15】部分的cover值大于1,与它前面的那一条扫描边的距离为10,【15,20】部分同理:
hdu 1542 Atlantis (线段树之扫描线)_第4张图片
插入第四条扫描线时计算面积为:
hdu 1542 Atlantis (线段树之扫描线)_第5张图片
具体代码实现如下:

#include
#include
#include
using namespace std;
struct LINE
{
    double x;
    double y_up;
    double y_down;
    int flag;
}line[220];
bool cmp(LINE a,LINE b)
{
    return a.xdouble y[220];
double x1,y1,x2,y2;
int n,index=0;
struct Tree
{
    double x,y_up,y_down;
    bool flag;//用来标记是否是叶子节点
    int cover;
}tree[1000*110];
void build(int i, int l, int r)
{
       tree[i].x = 0; //表示该节点上一条的x的值
       tree[i].cover = 0; //表示该区间上有多少条线段;左边线段加进去则++,右边线段加进去则--
       tree[i].y_down = y[l];
       tree[i].y_up = y[r];
       tree[i].flag = false;
       if(l+1==r)
       {
           tree[i].flag = true; //flag==true表示达到了叶子节点
           return;
       }
       int mid=(l+r)>>1;
       build(2*i, l, mid);
       build(2*i+1, mid, r);
}
double insert(int i,double x,double l,double r,int flag)
{
    if(r<=tree[i].y_down||l>=tree[i].y_up)///说明查找的这一条线不在当前节点的区间
        return 0;
    if(tree[i].flag)
    {
        if(tree[i].cover > 0)
        {
            double temp_x=tree[i].x;
            double ans=(x-temp_x)*(tree[i].y_up - tree[i].y_down);
            tree[i].x = x;   //定位上一次的x
            tree[i].cover += flag;
            return ans;
        }
        else
        {
            tree[i].cover += flag;
            tree[i].x = x;   //定位上一次的x
            return 0;
        }

    }
    double ans1, ans2;
    ans1 = insert(2*i, x, l, r, flag);
    ans2 = insert(2*i+1, x, l, r, flag);
    cout<' '<return ans1+ans2;

}
int main()
{
    int count1=0;
    while(scanf("%d",&n),n)
    {
        index=1;
        for(int i=1;i<=n;i++)
        {
           scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[index]=y1;
            line[index].x=x1;
            line[index].y_down=y1;
            line[index].flag=1;
            line[index++].y_up=y2;
            y[index]=y2;
            line[index].x=x2;
            line[index].y_down=y1;
            line[index].y_up=y2;
            line[index].flag=-1;
            index++;
        }
        sort(y+1,y+index);
        sort(line+1,line+index,cmp);
        build(1,1,index-1);
        double ans=0;
        for(int i=1;i<index;i++)
        {
            cout<' ';
            ans+=insert(1,line[i].x,line[i].y_down,line[i].y_up,line[i].flag);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++count1, ans);
    }
    return 0;
}

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