扫描线

题意:

二维平面有n个平行于坐标轴的矩形,现在要求出这些矩形的总面积. 重叠部分只能算一次.

分析:

线段树的典型扫描线用法.

首先假设有下图两个矩阵,我们如果用扫描线的方法如何计算它们的总面积呢?
在这里插入图片描述
首先我们将矩形的上下边分为上位边(即y坐标大的那条平行于x轴的边),和下位边(y坐标小的平行于x轴的边).然后我们把所有矩形的上下位边按照他们y坐标从小到大排序,可以得到4条扫描线:
在这里插入图片描述
又因为上面2个矩形有4个不同的浮点数x坐标,所以我们需要把x坐标离散化,这样才能用线段树来维护信息.所以我们这样离散化:
在这里插入图片描述
由上图可知,4个不同的x坐标把x轴分成了3段有效的区间.这里要注意我们线段树中每个叶节点(控制区间[L,L])不是指X[L]坐标,而是指区间[X[L],X[L+1]].线段树中其他节点控制的区间[L,R],也是指的x坐标轴的第L个区间到第R个区间的范围,也就是X[L]到X[R+1]坐标的范围.

然后我们Y坐标从小到大的顺序读入每条扫描线,并维护当前我们所读入的所有扫描线能有效覆盖X轴的最大长度sum[1].这里特别要注意如果我们读入的扫描线是矩形的下位边,那么我们就使得该范围的标记cnt位+1,如果是上位边,那么该范围的cnt就-1.所以如果cnt=0时,表示该节点控制的范围没有被覆盖,只要cnt!=0 就表示该节点控制的几块区间仍然被覆盖.

下面依次读入每条矩阵边,来一一分析,首先是读入第一条矩阵边:
在这里插入图片描述
我们读入了矩形1的下位边,那么该区域的cnt就+1=1了,所以该区域[10,20]就被覆盖了,然后可以推出整个区域被覆盖的长度是10.再根据第二条扫描线离第一条扫描线的高度差为5.所以不管你第二条扫描线是哪个矩形的什么边,或者能覆盖到X轴的什么范围,我上图中蓝色的矩形面积肯定是要算到总面积里面去的.即总面积ret+=sum[1]*(扫描线2的高度-扫描线1的高度). (想想看是不是这样).

下面读第二条扫描线:
在这里插入图片描述
由于第二条扫描线也是下位边,所以[15,20]和[20,25]的cnt+1.使得我们覆盖的范围变成了[10,25]了,并且第3条扫描线在20高度,所以这次我们必然增加的面积是上面深蓝色的长条=sum[1]*(扫描线3的高度-扫描线2的高度).

下面我们要读第三条扫描线了:
在这里插入图片描述
由于第三条扫描线是区间[10,20]的上位边,所以对应区间的cnt要-1,所以使得区间[10,15]的cnt=0了,而[15,20]区间的cnt-1之后变成了1.[20,25]的cnt仍然为1,不变.所以当前覆盖的有效x轴长度为10,即区间[15,25].所以增加的面积是图中褐色的部分.

到此,矩形的面积和就算出来了.由于对于任一矩形都是先读下位边(cnt+1),再读上位边(cnt-1)的,所以在更新线段树的过程中,任意节点的cnt都是>=0的.

例题

poj 1151 Atlantis

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <string>
#include <vector>

using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define PI acos(-1)
#define debug(a) cout << (a) << endl
typedef long long ll;
int dir8[8][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 1 }, { 1, -1 }, { -1, 1 }, { -1, -1 } };
int dir4[4][2] = { 1, 0, 0, 1, -1, 0, 0, -1 };
const int INF = 0x3f3f3f3fLL;
const long long LLF = 0x3f3f3f3f3f3f3f3fLL;
const int MAXn = 2e2 + 15;
const int mod = 1e9 + 7;
//priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;

struct node {
    double l, r, h;
    int d;
} line[MAXn];
int mark[MAXn<<2];
double sum[MAXn<<3];
double x_i[MAXn];

bool cmp_line(node a, node b)
{
    return a.h < b.h;
}

void init()
{
    mem(mark, 0);
    mem(sum, 0);
}

void pushup(int i, int l, int r)
{
    if (mark[i])
        sum[i] = x_i[r + 1 ] - x_i[l];
    else
        sum[i] = sum[i << 1] + sum[i << 1 | 1];
}

void update(int l, int r, int v, int i, int L, int R)
{
    if (l <= L && r >= R) {
        mark[i] += v;
        pushup(i, L, R);
        return;
    }
    int mid = (L + R) >> 1;
    if (l <= mid)
        update(l, r, v, i << 1, L, mid);
    if (r > mid)
        update(l, r, v, i << 1 | 1, mid + 1, R);
    pushup(i, L, R);
}
int main()
{
    int n, m, q, kase = 0;
    double x1, x2, y1, y2;
    while (cin >> q && q) {
        init();
        n = m = 0;
        for (int i = 0; i < q; i++) {
            cin >> x1 >> y1 >> x2 >> y2;
            x_i[++n] = x1;
            x_i[++n] = x2;
            line[++m] = node{ x1, x2, y1, 1 };
            line[++m] = node{ x1, x2, y2, -1 };
        }
        sort(x_i + 1, x_i + 1 + n);
        sort(line + 1, line + 1 + m, cmp_line);
        int k = unique(x_i + 1, x_i + 1 + n) - x_i - 1;
        double ans = 0;
        for (int i = 1; i < m; i++) {
            int l = lower_bound(x_i + 1, x_i + 1 + k, line[i].l) - x_i;
            int r = lower_bound(x_i + 1, x_i + 1 + k, line[i].r) - x_i-1;
            if (l <= r)
                update(l, r, line[i].d, 1, 1, k - 1);
            ans += sum[1] * (line[i + 1].h - line[i].h);
            //cout<<ans<<" "<<sum[1]<<" "<<line[i+1].h-line[i].h<<endl;
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n", ++kase, ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值