POJ 1151 Atlantis(线段树+扫描线)

59 篇文章 0 订阅
5 篇文章 0 订阅

Description
求n个矩形的面积并
Input
多组用例,每组用例第一行为一整数n表示矩形个数,之后n行每行四个浮点数x1,y1,x2,y2分别表示该矩形左下端点和右上端点的坐标,以n=0结束输入
Output
对于每组用例,输出这n个矩形的面积并
Sample Input
2
10 10 20 20
15 15 25 25.5
0
Sample Output
Test case #1
Total explored area: 180.00
Solution
首先将每个矩形用左右两边表示并标记每个线段是左边的边还是右边的边,然后对这些线段按x坐标升序排序,用一根线从左往右扫描这些线段,那么相邻两根扫描线之间围成的矩形面积之和即为面积并,那么我们只需要知道每个扫描线上被线段覆盖的长度,将其乘以相邻两根扫描线之间的距离即为扫描线扫过的面积,因此我们对y轴建一棵线段树(对所有线段的纵坐标离散化后建树),那么每条线段就可以用线段树中的一个区间来表示,每次扫到左边的边就在线段树中覆盖这条边对应的区间,扫到右边的边就在线段树中取消对这条边的覆盖,每扫过一根线段就维护一下扫描线被覆盖的长度即可
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 222
#define eps 1e-8
struct Tree
{
    int left,right,num;//num记录这个区间被覆盖的次数 
    double len;//len记录这个区间被覆盖的长度 
}T[4*maxn];
struct Line
{
    double x,y1,y2;
    bool flag;
}line[maxn];
int n,cnt,res;
double y[maxn],ans,len;
bool equal(double x,double y)
{
    return fabs(x-y)<eps;
}
void add(double x,double y1,double y2,double yy,int flag)
{
    line[cnt].x=x;line[cnt].y1=y1;line[cnt].y2=y2;line[cnt].flag=flag;y[cnt++]=yy;
}
bool cmp(Line l1,Line l2)
{
    if(l1.x!=l2.x) return l1.x<l2.x;
    return l1.flag>l2.flag;
}
void build(int l,int r,int t)
{
    T[t].left=l;
    T[t].right=r;
    T[t].num=0;
    T[t].len=0;
    if(l+1==r)return ;
    int mid=(l+r)>>1;
    build(l,mid,2*t);
    build(mid,r,2*t+1);
}
void update_len(int t)
{
    if(T[t].num) T[t].len=y[T[t].right]-y[T[t].left];
    else if(T[t].left+1==T[t].right) T[t].len=0;
    else T[t].len=T[2*t].len+T[2*t+1].len;
}
void update(double l,double r,int t,int x)
{
    if(equal(y[T[t].left],l)&&equal(y[T[t].right],r)) T[t].num+=x;
    if(T[t].left+1<T[t].right)
    {
        int mid=(T[t].left+T[t].right)>>1;
        if(r<=y[mid]+eps) update(l,r,2*t,x);
        else if(l>=y[mid]-eps) update(l,r,2*t+1,x);
        else
        {
            update(l,y[mid],2*t,x);
            update(y[mid],r,2*t+1,x);
        }
    }
    update_len(t);
}
int main()
{
    int t=1;
    while(~scanf("%d",&n),n)
    {
        cnt=0;ans=0;len=0;
        double x1,x2,y1,y2;
        while(n--)
        {
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            //将每个矩形用左右两边表示 
            add(x1,y1,y2,y1,1);
            add(x2,y1,y2,y2,0); 
        }
        sort(line,line+cnt,cmp);//对得到的线段按x升序排序 
        //对y轴建树 
        sort(y,y+cnt);
        res=unique(y,y+cnt)-y;
        build(0,res-1,1);
        for(int i=0;i<cnt;i++)
        {
            if(line[i].flag) update(line[i].y1,line[i].y2,1,1);//扫到左边的边就在线段树中覆盖这部分 
            else update(line[i].y1,line[i].y2,1,-1);//扫到右边的边就在线段树中取消这部分的覆盖 
            if(i) ans+=len*(line[i].x-line[i-1].x);//累加每次扫描线扫过的面积 
            len=T[1].len;//维护扫描线上被覆盖的长度 
        }
        printf("Test case #%d\n",t++);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值