POJ1151--Atlantis(离散化+扫描线)

题目大意:给出n个矩形,求面积并


分析:线段树+离散化+扫描线。

先以Y轴(横线)建立线段树,当然,这里需要离散化。

然后,用一条直线从左到右扫描,碰到一条矩形竖边的时候,就计算该直线有多长被矩形覆盖,以及被覆盖部分是覆盖了几重。碰到矩形左边,要增加被覆盖的长度,碰到右边,要减少被覆盖的长度随着扫描线的右移动,覆盖面积不断增加。每碰到一条矩形的纵边,覆盖面积就增加( 此时扫描线被矩形覆盖的长度* 该纵边到下一条纵边的距离)。


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

struct Seg{
    double x, y1, y2;
    bool left;  //是否是矩形的左边
    Seg(){}
    Seg(double x, double y1, double y2, bool left):x(x), y1(y1), y2(y2) ,left(left){}
    bool operator < (const Seg & s1) const {
        return x < s1.x;
    }
}seg[210];

int n;
double y[210];
double len[1000];   //当前区间覆盖长度
int cover[1000]; //当前区间被几个矩形覆盖
int yc, lc;

void PushUp(int root) {
    if(cover[root] == 0)    如果不为0,则说明当前区间仍然被某个矩形完全覆盖,则不能更新len
        len[root] = len[2*root+1]+len[2*root+2];
    return;
}

void Insert(int root, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        len[root] = y[r+1] - y[l];  //区间编号加1,就是横线的位置
        cover[root]++;
        return;
    }
    int m = (l+r)/2;
    if(L <= m)
        Insert(2*root+1, l, m, L, R);
    if(R > m)
        Insert(2*root+2, m+1, r, L, R);
    PushUp(root);
    return;
}

void Delete(int root, int l, int r, int L, int R) {
    if(L <= l && r <= R) {
        cover[root]--;
        if(cover[root] == 0) {
            if(l == r)
                len[root] = 0;
            else
                PushUp(root);
        }
        return;
    }
    int m = (l+r)/2;
    if(L <= m)
        Delete(2*root+1, l, m, L, R);
    if(R > m)
        Delete(2*root+2, m+1, r, L, R);
    PushUp(root);
    return;
}

int Bin(double v) {     //在区间[l,r)中查找v,找不到就返回 yc,若返回的是yc,则说明已经是最后一条直线了
    int l = 0, r = yc-1;
    while(l <= r) {
        int m = (l+r)/2;
        if(y[m] == v) return m;
        else if(y[m] > v) r = m-1;
        else l = m+1;
    }
    return yc;
}

void Build(int root, int l, int r) {
    len[root] = cover[root] = 0;
    if(l == r) return;
    Build(2*root+1, l, (l+r)/2);
    Build(2*root+2, (l+r)/2+1, r);
    return;
}

int main() {
    int t = 0;
    while(scanf("%d", &n) && n) {
        double area = 0;
        yc = 0, lc = 0;
        while(n--) {
            double x1, y1, x2, y2;
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            y[yc++] = y1, y[yc++] = y2;
            seg[lc++] = Seg(x1, y1, y2, true);
            seg[lc++] = Seg(x2, y1, y2, false);
        }
        sort(y, y+yc);
        sort(seg, seg+lc);
        yc = unique(y, y+yc)-y;
        Build(0, 0, yc-1-1);
        for(int i = 0; i < lc-1; i++) {
            int L = Bin(seg[i].y1);
            int R = Bin(seg[i].y2)-1;  //树上的一个点其实是表示一个区间,所以,得出的位置减1就是区间编号
            if(seg[i].left)
                Insert(0, 0, yc-2, L, R);
            else
                Delete(0, 0, yc-2, L, R);
            area += len[0]*(seg[i+1].x-seg[i].x);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++t, area);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值