poj 1151 线段树+扫描线(Atlantis矩形面积)

题意:给定n个矩形 (n<= 100),其顶点坐标是浮点数,可能互相重叠,问这些矩形覆盖到的面积是多大。

思路:线段树+扫描线。用一条直线从左到右扫描,碰到一条矩形竖边的时候,就计算该直线有多长被矩形覆盖,以及被覆盖部分是覆盖了几重。碰到矩形左边,要增加被覆盖的长度,碰到右边, 要减少被覆盖的长度。每碰到一条矩形的纵边,覆盖面积就增加 Len * 该纵边到下一条纵边的距离。Len是此时扫描线被矩形覆盖的长度。

在Y轴进行离散化。n个矩形的2n个横边纵 坐标共构成最多2n-1个区间的边界,对这些区间编号,建立起线段树。

注意灵活运用stl提供的函数。

unique的作用是从输入序列中“删除”所有相邻的重复元素,即去重。

lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置,在本题中即返回查找到的y数组下标。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cstdlib>
using namespace std;
#define clc(s,t) memset(s,t,sizeof(s))
#define INF 0x3fffffff
#define N 100005
struct line{                //表示矩形的竖边
    double x,y1,y2;
    int l;                  //表示是否为矩形的左边
    bool operator<(const line &b) const{
        return x<b.x;
    }
}line[N<<1];
struct tree{
    int l,r;
    int num;                //覆盖的重数
    double len;
}t[N<<3];
double y[N<<1];
int ylen;
int T = 1,n;
int mid(int a,int b){
    return (a+b)>>1;
}
void build(int r,int left,int right){
    t[r].l = left;
    t[r].r = right;
    t[r].num = t[r].len = 0;
    if(left == right)
        return;
    build(r*2, left, mid(left, right));
    build(r*2+1, mid(left, right)+1, right);
}
void insert(int r,int left,int right){
    int mm = mid(t[r].l,t[r].r);
    if(t[r].l == left && t[r].r == right){
        t[r].len = y[right]-y[left-1];
        t[r].num++;
        return;
    }
    if(right<=mm)
        insert(r*2, left, right);
    else if(left>mm)
        insert(r*2+1, left, right);
    else{
        insert(r*2, left, mm);
        insert(r*2+1, mm+1, right);
    }
    if(t[r].num == 0)
        t[r].len = t[r*2].len + t[r*2+1].len;
}
void del(int r,int left,int right){
    int mm = mid(t[r].l, t[r].r);
    if(t[r].l == left && t[r].r == right){
        t[r].num--;
        if(t[r].num == 0){
            if(left == right)
                t[r].len = 0;
            else
                t[r].len = t[r*2].len+t[r*2+1].len;
        }
        return;
    }
    if(right <= mm)
        del(r*2,left,right);
    else if(left > mm)
        del(r*2+1,left,right);
    else{
        del(r*2,left,mm);
        del(r*2+1,mm+1,right);
    }
    if(t[r].num == 0)
        t[r].len = t[r*2].len+t[r*2+1].len;
}
int main(){
    while(scanf("%d",&n) && n){
        int i,j,y1,y2;
        double a,b,c,d,res=0;
        ylen = 0;
        for(i = 0;i<n;i++){
            scanf("%lf %lf %lf %lf",&a,&b,&c,&d);
            y[ylen++] = b;
            y[ylen++] = d;
            line[i*2].x = a;
            line[i*2].l = 1;
            line[i*2+1].l = 0;
            line[i*2+1].x = c;
            line[i*2].y1 = line[i*2+1].y1 = b;
            line[i*2].y2 = line[i*2+1].y2 = d;
        }
        sort(line,line+2*n);
        sort(y,y+ylen);
        ylen = unique(y, y+ylen)-y;
        build(1,1,ylen-1);          //按照区间的个数建树
        for(i = 0;i<2*n-1;i++){
            y1 = lower_bound(y,y+ylen,line[i].y1)-y;
            y2 = lower_bound(y,y+ylen,line[i].y2)-y;
            if(line[i].l)           //注意,y[0]~y[1]是区间1
                insert(1,y1+1,y2);
            else
                del(1,y1+1,y2);
            res += t[1].len*(line[i+1].x - line[i].x);
        }
        printf("Test case #%d\n",T++);
        printf("Total explored area: %.2lf\n\n",res);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值