扫描线+线段树优化(洛谷8648)

文章讲述了在X星球考古过程中,机器人团队根据测量结果报告优先考古的矩形区域,这些矩形可能存在重叠。问题转化为计算所有矩形覆盖的总面积,通过离散化和树状数据结构实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

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

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

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

每个机器人都各有特长、身怀绝技。它们感兴趣的内容也不相同。

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

矩形的表示格式为 (x1​,y1​,x2​,y2​),代表矩形的两个对角点坐标。

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

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

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

注意,各个矩形间可能重叠。

本题的输入为若干矩形,要求输出其覆盖的总面积。

输入格式

第一行,一个整数 n,表示有多少个矩形 (1≤n<10000)。

接下来的 n 行,每行有 44 个整数 x1​,y1​,x2​,y2​,空格分开,表示矩形的两个对角顶点坐标。

(0≤x1​,y1​,x2​,y2​≤10000)。

输出格式

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

思路:

选择平行x轴或y轴的一条直线(我选的y轴),让他沿着x轴方向扫过去,我们很容易发现面积等于

每一段y轴长度len不变时,各段的len*(x2-x1和;所以我们需要维护y轴上的长度,我们将所有y值放进一个数组y[n<<1],排序,去重,完成离散化。建树时每个节点维护的是y的范围区间,叶子节点维护的是y[i]到y[i+1]区间长度,每个节点还要记录y[i],y[i+1],这个区间重复的次数,当节点被覆盖次数>0,那么这长度为y[i+1]-y[i],否则为其两个子节点长度和。

代码:

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <queue>
#include<algorithm>
#include<cmath>
#include<map>
#include<string.h>
#include<string>
#include<cstring>
using namespace std;
const long long N = 1e4 + 1000;
const long long MAX = 1e9;
#define ls (o<<1)
#define rs (o<<1|1)
struct tree
{
    int l;
    int r;
    int cnt;
    long long  len;
}tr[N << 3];
struct edge
{
    int x;
    int y1;
    int y2;
    int f;
    bool operator < (edge& b)
    {
        return x < b.x;
    }

}eg[N << 2];
int n, y[N << 1];
void build(int o, int l, int r)
{
    tr[o].l = y[l];
    tr[o].r = y[r];
    if (r == l + 1) return;
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid, r);
}
void pushup(int o)
{
    if (tr[o].cnt) tr[o].len = tr[o].r - tr[o].l;
    else
        tr[o].len = tr[ls].len + tr[rs].len;
}
void change(int o, int a, int b, int f)
{
    if (a >= tr[o].r || b <= tr[o].l) return;
    if (a <= tr[o].l && b >= tr[o].r)
    {
        tr[o].cnt += f;
        pushup(o);
        return;
    }
    change(ls, a, b, f);
    change(rs, a, b, f);
    pushup(o);
}
int main()
{
    int x1, x2, y1, y2;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        eg[2 * i - 1] = { x1,y1,y2,1 };
        eg[2 * i] = { x2,y1,y2,-1 };
        y[2 * i - 1] = y1;
        y[2 * i] = y2;
    }
    n *= 2;
    sort(y + 1, y + 1 + n);
    sort(eg + 1, eg + 1 + n);
    build(1, 1, n);
    long long ans = 0;
    for (int i = 1; i < n; i++)
    {
        change(1, eg[i].y1, eg[i].y2, eg[i].f);
        ans += (long long)(eg[i + 1].x - eg[i].x) * tr[1].len;
    }
    cout << ans << endl;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值