HDU 1542 Atlantis(扫描线)题解

题意:给n个可能相交的矩形,问你不重复的总面积

思路:扫描线,一边扫一边加。

扫描线:图片来源:理解扫描线

假设我们要算以下四个矩形面积,显然中间深色的是重复的。我们按照x的大小,从左往右扫,然后用线段树维护y轴向的长度就可以了。但是,我们不能用点去维护y轴坐标,而是抽象成把点i看成y[i]到y[i+1]这个区间,不然会有错。

代码:

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<queue>
#include<vector>
#include<string>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<iostream>
#include<algorithm>
typedef long longll;
using namespace std;
const int maxn = 100 + 10;
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
struct Bian{
    double x, y1, y2;
    int flag;
    bool operator < (const Bian &a) const{
        return x < a.x;
    }
}bian[maxn << 1];
double y[maxn << 1];    //离散化定位
int n, cnt;
int Find(double x){ //在y中离散化后位置
    int l = 1, r = cnt, ans = 0;
    while(l <= r){
        int m = (l + r) >> 1;
        if(fabs(y[m] - x) < 1e-7) ans = m;
        if(y[m] > x) r = m - 1;
        else l = m + 1;
    }
    return ans;
}
int cover[maxn << 3]; //y的覆盖次数
double sum[maxn << 3];
void pushUp(int l, int r, int rt){
    if(cover[rt] > 0) sum[rt] = y[r + 1] - y[l];
    //全覆盖了
    else if(l == r) sum[rt] = 0;
    //没覆盖的叶子结点
    else sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    //部分覆盖
}
void build(int l, int r, int rt){
    if(l == r){
        cover[rt] = sum[rt] = 0;
        return;
    }
    int m = (l + r) >> 1;
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    cover[rt] = sum[rt] = 0;
}
void update(int L, int R, int l, int r, int v, int rt){
    if(L <= l && R >= r){
        cover[rt] += v;
        pushUp(l, r, rt);
        return;
    }
    int m = (l + r) >> 1;
    if(L <= m)
        update(L, R, l, m, v, rt << 1);
    if(R > m)
        update(L, R, m + 1, r, v, rt << 1 | 1);
    pushUp(l, r, rt);
}
int main(){
    int ca = 1;
    while(scanf("%d", &n) && n){
        for(int i = 1; i <= n; i++){
            double x1, x2, y1, y2;
            scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            int ll = 2 * i - 1, rr = 2 * i;
            bian[ll].x = x1, bian[ll].y1 = y1, bian[ll].y2 = y2;
            bian[ll].flag = 1;
            bian[rr].x = x2, bian[rr].y1 = y1, bian[rr].y2 = y2;
            bian[rr].flag = -1;
            y[ll] = y1, y[rr] = y2; //定位用的
        }
        n = n + n;
        sort(bian + 1, bian + n + 1);
        sort(y + 1, y + n + 1);

        cnt = 1;    //unique
        for(int i = 2; i <= n; i++){
            if(y[i] != y[i - 1]){
                y[++cnt] = y[i];
            }
        }

        double ans = 0;
        build(1, cnt, 1);
        for(int i = 1; i < n; i++){
            update(Find(bian[i].y1), Find(bian[i].y2) - 1, 1, cnt, bian[i].flag, 1);
            ans += sum[1] * (bian[i + 1].x - bian[i].x);
        }
        printf("Test case #%d\n", ca++);
        printf("Total explored area: %.2lf\n\n", ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/KirinSB/p/10740033.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值