P5490 【模板】扫描线

【模板】扫描线

题目描述

求 n 个四边平行于坐标轴的矩形的面积并。

输入格式

第一行一个正整数 n。

接下来 𝑛n 行每行四个非负整数x1​,y1​,x2​,y2​,表示一个矩形的四个端点坐标为 (𝑥1,𝑦1),(𝑥1,𝑦2),(𝑥2,𝑦2),(𝑥2,𝑦1)。

think: 维护一个带懒标记的线段树

struct node {
    int l, r;       //左右端点
    int minv, cnt;  //区间最小值,区间最小值出现的次数
    int tag;        //懒标记.本题特殊情况只支持区间+1, -1.
}tr[N * 8];

由于本题四边形边界会到1e9,所以还需要离散化,这里就离散化x坐标了(也可以离散化y坐标)

    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());

find用来查询离散化后下标对应的值

int find(int y)
{
    return lower_bound(alls.begin(), alls.end(), y) - alls.begin() + 1;
}

最后,讲一下为什么可以这样做,我们每次查询tr[1]这个区间,如果这个区间的最小值不是0,说明整个区间都被覆盖了.反之,当前区间被覆盖的长度就是区间长度-当前区间最小值的个数.

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define int long long
const int N = 1e6 + 10;
typedef pair<int, int> pii;

int n, m;
vector<int> alls;
vector<array<int, 4>> event;

struct node {
    int l, r;       
    int minv, cnt;  
    int tag;        
}tr[N * 8];

int find(int y)
{
    return lower_bound(alls.begin(), alls.end(), y) - alls.begin() + 1;
}

void pushup(node &root, node &l, node &r) {
    if (l.minv == r.minv) {
        root.cnt = l.cnt + r.cnt;
        root.minv = l.minv;
    } else if (l.minv < r.minv) {
        root.cnt = l.cnt;
        root.minv = l.minv;
    } else {
        root.cnt = r.cnt;
        root.minv = r.minv;
    }
}

void pushup(int u) {
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

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

void pushdown(int u) {
    if (tr[u].tag) {
        tr[u << 1].tag += tr[u].tag;
        tr[u << 1 | 1].tag += tr[u].tag;
        tr[u << 1].minv += tr[u].tag;
        tr[u << 1 | 1].minv += tr[u].tag;
        tr[u].tag = 0;
    }
}

void modify(int u, int l, int r, int v) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].minv += v;
        tr[u].tag += v;
    } else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (r <= mid) modify(u << 1, l, r, v);
        else if (l > mid) modify(u << 1 | 1, l, r, v);
        else {
            modify(u << 1, l, r, v);
            modify(u << 1 | 1, l, r, v);
        }
        pushup(u);
    }
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ ) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        alls.emplace_back(x1);
        alls.emplace_back(x2);
        event.push_back({y1, 1, x1, x2});
        event.push_back({y2, -1, x1, x2});
    }    
    sort(event.begin(), event.end());
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    m = alls.size() - 1;
    build(1, 1, m);
    int totlen = alls[m] - alls[0], pre = 0, ans = 0;
    for (auto evt : event) {
        int now = totlen;
        if (tr[1].minv == 0) now = totlen - tr[1].cnt;
        ans += (evt[0] - pre) * now;
        pre = evt[0];
        int x1 = find(evt[2]), x2 = find(evt[3]) - 1;
        modify(1, x1, x2, evt[1]);
    }
    cout << ans << '\n';
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int _ = 1;
    // cin >> _;
    while (_ -- )
    {
        solve();
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值