HDU_1542_线段树【扫描线】

Atlantis

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


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
 
记得最开始学线段树的时候就看过这道题,我是完全懵逼的,本以为有生之年也不能弄懂这道题的思路,结果时至今日,总算是弄懂了传说中的扫描线。
 

struct segment       //这就是传说中的扫描线
{
  double l,r,h;     //l,r是左右端点,h为高度
  int f;     //上边f为-1,下边为1
}ss[2*MAXN];      //ss存的是所有矩形的上下边的信息

 
思路:
扫描线从下往上扫,所以离散化横坐标,因为我们每扫到一条边,就要update,即将这条边投影到总区间上,那么怎么update呢,线段上的点的坐标是double,所以我们离散化横坐标。用一个pos数组存储每个点的横坐标,下标为第几个点,我们将pos从小到大排序并去重,去重后的点个数为m,此时就可以建树了build(0,m-1,1);为什么这样建树,仔细一想应该能明白。
将ss(边)按高度升序排序,遍历,遍历到一条边,则在pos中搜索其左右端点在pos中的下标。如果是下边,将其投影到总区间,若是上边,则在总区间中将其对应下边的投影去掉。然后加上此次算出的一块面积。
 
 
注意:
扫描线段时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就很好理解了   
#include<cstdio>
#include<set>
#include<iostream>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<string>
#include<cstring>
using namespace std;
#define MAXN 105
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1

struct segment
{
    double l,r,h;
    int f;
}ss[2*MAXN];

struct Node
{
    int l,r;
    int cnt;
    double len;
    int mid()
    {
        return (l+r>>1);
    }
}tt[2*MAXN*4];

double pos[2*MAXN];
int nums;

bool cmp(segment a,segment b)
{
    return a.h<b.h;
}

void build(int l,int r,int rt)
{
    tt[rt].l=l;
    tt[rt].r=r;
    tt[rt].cnt=0;
    tt[rt].len=0;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}

int binary(double key,int l,int r)
{
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(pos[mid]==key)
            return mid;
        else if(key<pos[mid])
            r=mid-1;
        else
            l=mid+1;
    }
    return -1;
}

void get_len(int rt)
{
    if(tt[rt].cnt)
        tt[rt].len=pos[tt[rt].r+1]-pos[tt[rt].l];
    else if(tt[rt].l==tt[rt].r)
        tt[rt].len=0;
    else
        tt[rt].len=tt[rt<<1].len+tt[rt<<1|1].len;
}

void update(int l,int r,int val,int rt)
{
    if(tt[rt].l==l&&tt[rt].r==r)
    {
        tt[rt].cnt+=val;
        get_len(rt);
        return;
    }
    int mid=tt[rt].mid();
    if(r<=mid)
        update(l,r,val,rt<<1);
    else if(l>mid)
        update(l,r,val,rt<<1|1);
    else
    {
        update(l,mid,val,rt<<1);
        update(mid+1,r,val,rt<<1|1);
    }
    get_len(rt);
}

int main()
{
    int cas=0;
    int n;
    while(scanf("%d",&n)!=EOF&&n)
    {
        nums=0;
        for(int i=0;i<n;i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            ss[nums].l=x1;ss[nums].r=x2;ss[nums].h=y1;ss[nums].f=-1;
            ss[nums+1].l=x1;ss[nums+1].r=x2;ss[nums+1].h=y2;ss[nums+1].f=1;
            pos[nums]=x1;pos[nums+1]=x2;
            nums+=2;
        }
        sort(ss,ss+nums,cmp);
        sort(pos,pos+nums);
        int m=1;
        for(int i=1;i<nums;i++)
            if(pos[i]!=pos[i-1])
                pos[m++]=pos[i];
        build(0,m-1,1);
        double ans=0;
        for(int i=0;i<nums;i++)
        {
            int l=binary(ss[i].l,0,m-1);
            int r=binary(ss[i].r,0,m-1)-1;
            update(l,r,ss[i].f,1);
            ans+=(ss[i+1].h-ss[i].h)*tt[1].len;
        }
        printf("Test case #%d
",++cas);
        printf("Total explored area: %.2f

",ans);
    }
    return 0;
}
 
原文地址:https://www.cnblogs.com/jasonlixuetao/p/5994577.html