油漆面积(扫描线+线段树)

题面

X星球的一批考古机器人正在一片废墟上考古。

该区域的地面坚硬如石、平整如镜。

管理人员为方便,建立了标准的直角坐标系。

每个机器人都各有特长、身怀绝技。

它们感兴趣的内容也不相同。

经过各种测量,每个机器人都会报告一个或多个矩形区域,作为优先考古的区域。

矩形的表示格式为 (x1,y1,x2,y2)

,代表矩形的两个对角点坐标。

为了醒目,总部要求对所有机器人选中的矩形区域涂黄色油漆。

小明并不需要当油漆工,只是他需要计算一下,一共要耗费多少油漆。

其实这也不难,只要算出所有矩形覆盖的区域一共有多大面积就可以了。

注意,各个矩形间可能重叠。
输入格式

第一行,一个整数 n

,表示有多少个矩形。

接下来的 n
行,每行有 4 个整数 x1,y1,x2,y2

,空格分开,表示矩形的两个对角顶点坐标。
输出格式

一行一个整数,表示矩形覆盖的总面积。
数据范围

1≤n≤10000,
0≤x1,x2,y2,y2≤10000
数据保证 x1<x2 且 y1<y2。
输入样例1:

3
1 5 10 10
3 1 20 20
2 7 15 17

输出样例1:

340

输入样例2:

3
5 2 10 6
2 7 12 10
8 1 15 15

输出样例2:

128

思路

看了题解才知道这是线段树中比较特殊的一种做法,线段树中的每一个点都表示一个一段关于y的区间, 我们只需要用沿着x轴扫描即可得出答案, 但一个关键点就是如何记录重复的y区间, 并且再使用完后再更新它。细节见于代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 10010;
int n;
struct Segment
{
    int x, y1, y2;
    int k;// 起始边为1, 终边为0
    bool operator <(const Segment &t) const
    {
        return x < t.x;
    }
}seg[N * 2];

struct node
{
    int l, r; 
    int cnt, len; // cnt为覆盖这个区间(l~r)的次数
    //len表示覆盖的长度(如果cnt>=1那么len == 该区间长度)
}tr[N * 4];

void build(int u, int l, int r)
{
    tr[u] = {l, r};
    if(l == r) return;
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u<<1|1, mid + 1, r);
}

void pushup(int u) // keypoint
{
    if(tr[u].cnt > 0) tr[u].len = tr[u].r - tr[u].l + 1;
    //如果它这个区间任然被完整覆盖过一次直接就等于区间长度即可
    else if(tr[u].l == tr[u].r) tr[u].len = 0;// 到了叶子节点, 如果被覆盖那么cnt必定大于0反之久没有被覆盖,这是树上的特例
    else tr[u].len = tr[u<<1].len + tr[u << 1|1].len;//如果没有被完整覆盖直接等于儿子们的长度之和即可
}
void modify(int u, int l, int r, int k)
{
    if(tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].cnt += k;
        pushup(u); //此处是更新
        // 如果k是-1使tr[u].cnt == 0, 那他肯定要找之前可能还有的长度, 即使这个长度也不能将这个区间覆盖完,也要向上更新
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        if(l <= mid) modify(u<<1, l, r, k);
        if(r > mid) modify(u<<1|1, l, r, k);
        pushup(u);
    }
}
int main()
{
    scanf("%d", &n);
    int m = 0;
    for(int i = 0; i < n; i++)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        seg[m ++] = {x1, y1, y2, 1};
        seg[m ++] = {x2, y1, y2, -1};
    }
    sort(seg, seg + m);
    build(1, 0, 10000);

    int res = 0;
    for(int i = 0; i < m; i++)
    {
        if(i > 0) res += tr[1].len * (seg[i].x - seg[i - 1].x);
        modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].k);
    }
    printf("%d\n", res);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值