码迷,mamicode.com
首页 > 其他好文 > 详细

HDU 1542 Atlantis(线段树扫描线+离散化求面积的并)

时间:2016-11-03 18:31:22      阅读:189      评论:0      收藏:0      [点我收藏+]

标签:man   namespace   难点   each   mon   uri   log   for   ros   

Atlantis

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

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
 

 

题目链接:HDU 1542

感觉难点就在如何用点树实现线段上的统计,首先这题是浮点数,肯定要离散化建树(我选的是对X坐标离散化),二分寻找下标当作区间[l,r]。

现在假设有两个区间段:1——2——3 与 3——4——5,但是如果用点树的方式进行更新对前面一个区间+1,后面一个区间-1,那会造成3这个点的覆盖次数变成0,但显然这两个区间在“段”上是连续的,这样更新肯定会出现问题,但是习惯上是写点树而不是段树,那只能对点修改一下,把更新区间的右端R减掉1,每个点表示这个点到后一个点的一段因此前面的两个区间变成了[1,2]与[3,4],这样在点上就不会出现重复的问题了,然后另外一点改动就是在pushup时直接向上传递不用pushdown。

举个例子

$$\begin{array}{c|lll}
{下标}&{0}&{1}&{2}&{3}&{4}&{5}\\
\hline
{实际值}&{3.3}&{9.8}&{12.1}&{19.8}&{24.9}&{33.3}\\
\end{array}$$

这样的一个坐标离散化这时候出现一个线段[9.8~12.1],然后对应离散化的是[1,2-1]即[1,1],但统计len的时候显然是用12.1-9.8=2.3,因此统计时要用X[R+1]-X[L]来作为长度即X[2]-X[1],那这看起来似乎跟前面的刻意把右端点改成R-1矛盾了……既然统计要R+1那前面干嘛要R-1,其实R-1为的是不影响线段树的区间覆盖,但是你是知道实际上要用R来算,由于统计时是不会影响区间覆盖的,因此要还原回去即R=(R-1)+1

代码:

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
typedef pair<int,int> pii;
typedef long long LL;
const double PI=acos(-1.0);
const int N=110;
struct seg
{
    int l,mid,r;
    int cnt;
    double len;
};
struct Line
{
    double l,r,h,flag;
    bool operator<(const Line &t)const
    {
        return h<t.h;
    }
};
seg T[N<<3];
Line line[N<<1];
double xpos[N<<1];

inline void pushup(int k)
{
    if(T[k].cnt)
        T[k].len=xpos[T[k].r+1]-xpos[T[k].l];
    else
    {
        if(T[k].l==T[k].r)
            T[k].len=0;
        else
            T[k].len=T[LC(k)].len+T[RC(k)].len;
    }
}
void build(int k,int l,int r)
{
    T[k].l=l;
    T[k].r=r;
    T[k].mid=MID(l,r);
    T[k].len=0.0;
    T[k].cnt=0;
    if(l==r)
        return ;
    build(LC(k),l,T[k].mid);
    build(RC(k),T[k].mid+1,r);
}
void update(int k,int l,int r,int flag)
{
    if(l<=T[k].l&&T[k].r<=r)
    {
        T[k].cnt+=flag;
        pushup(k);
    }
    else
    {
        if(r<=T[k].mid)
            update(LC(k),l,r,flag);
        else if(l>T[k].mid)
            update(RC(k),l,r,flag);
        else
            update(LC(k),l,T[k].mid,flag),update(RC(k),T[k].mid+1,r,flag);
        pushup(k);
    }
}
int main(void)
{
    int n,i,q=1;
    double xa,xb,ya,yb;
    while (~scanf("%d",&n)&&n)
    {
        int cnt_line=0;
        for (i=0; i<n; ++i)
        {
            scanf("%lf%lf%lf%lf",&xa,&ya,&xb,&yb);
            xpos[cnt_line]=xa;
            line[cnt_line]=(Line){xa,xb,ya,1};
            ++cnt_line;
            xpos[cnt_line]=xb;
            line[cnt_line]=(Line){xa,xb,yb,-1};
            ++cnt_line;
        }
        sort(xpos,xpos+cnt_line);//X轴坐标排序
        sort(line,line+cnt_line);//线段排序

        build(1,0,cnt_line);

        double res=0.0,dh;
        int l,r;
        for (i=0; i<cnt_line-1; ++i)
        {
            l=lower_bound(xpos,xpos+cnt_line,line[i].l)-xpos;
            r=lower_bound(xpos,xpos+cnt_line,line[i].r)-xpos;
            update(1,l,r-1,line[i].flag);
            dh=line[i+1].h-line[i].h;
            res+=dh*T[1].len;
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",q++,res);
    }
    return 0;
}

HDU 1542 Atlantis(线段树扫描线+离散化求面积的并)

标签:man   namespace   难点   each   mon   uri   log   for   ros   

原文地址:http://www.cnblogs.com/Blackops/p/6027209.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!