P - Atlantis (线段树+扫描线)

 
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. 

InputThe 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.OutputFor 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 
题解:扫描线常规操作,如果不会先看大神的讲解再来:https://blog.csdn.net/xianpingping/article/details/83032798
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;
double x[maxn];
struct node
{
    double l,r,h;
    int d;
    bool operator < (const node &a)const//按纵坐标从小到大排序
    {
        return h<a.h;
    }
}line[maxn];
int cnt[maxn<<2];//cnt[rt]表示该节点的覆盖次数,只要不为0就是被覆盖过
double sum[maxn<<2];//线段树sum[rt]中每一个节点都是一个区间,sum[rt]表示该节点区间中的总有效覆盖长度
double area;
void pushup(int l,int r,int rt)
{
    if(cnt[rt])//如果该节点(就是一个区间)全部被覆盖,那么sum就是这个区间的长度了~
        sum[rt]=x[r+1]-x[l];//x的下标是点,对于线段树的一个区间要进行右边下标+1再减去左边
    else    //否则要进行左右儿子的加和,因为不连续啊~
        sum[rt]=sum[rt*2]+sum[rt*2+1];
}
void update(int L,int R,int v,int l,int r,int rt)//在L,R区间覆盖次数加上v,即如果v为1就加1,为-1就减1
{
    if(L<=l&&R>=r)
    {
        cnt[rt]+=v;
        pushup(l,r,rt);//改变了cnt数组后要重新pushup
        return ;
    }
    int mid=(l+r)/2;//下面常规操作~
    if(L<=mid)
        update(L,R,v,l,mid,rt*2);
    if(R>=mid+1)
        update(L,R,v,mid+1,r,rt*2+1);
    pushup(l,r,rt);
}
int main()
{
    int t,k=1;
    while(cin>>t&&t)
    {
        memset(cnt,0,sizeof(cnt));//初始化全部为0
        memset(sum,0,sizeof(sum));
        area=0;     //求得面积
        int n=0,m=0;//x数组得最大元素个数,line数组得最大元素个数
        for(int i=1;i<=t;i++)//记录信息
        {
            double x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            x[++n]=x1;
            x[++n]=x2;
            line[++m]=(node){x1,x2,y1,1};
            line[++m]=(node){x1,x2,y2,-1};
        }
        sort(x+1,x+1+n);//对x数组从小打大排序
        sort(line+1,line+1+m);//对line扫描线数组按高度(即y)从小到大排序
        int r=unique(x+1,x+1+n)-x-1;//r个不同的x将一条线段分成r-1个小区间,这也是后面查询总区长度是1到r-1和R--的原因
        for(int i=1;i<m;i++)//这里是对扫描线进行扫描~,扫描m-1次即可
        {
            int L=lower_bound(x+1,x+r+1,line[i].l)-x;
            int R=lower_bound(x+1,x+r+1,line[i].r)-x;
            R--;//每一个节点是一个区间,这里是将点转化为区间的操作
            update(L,R,line[i].d,1,r-1,1);
            area+=sum[1]*(line[i+1].h-line[i].h);//用总有效长度乘上两条扫描线之间的距离即得到该块面积
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",k++,area);
    }
    return 0;
}

 

 
posted @ 2019-03-20 09:28  cherish__lin  阅读(356)  评论(0编辑  收藏  举报