洛谷5490:【模板】扫描线(线段树求矩形面积并)

洛谷5490:【模板】扫描线(线段树求矩形面积并)

题意描述:

  • \(n\)个矩形的面积并。

输入格式:

  • 第一行输入一个整数\(n\)
  • 接下来\(n\)行每行输入四个非负整数\(x_1,y_1,x_2,y_2\),表示一个矩形的左下角坐标为\((x_1,y_1)\),右上角坐标为\((x_2,y_2)\)

输出格式:

  • 输出一个正整数表示,表示\(n\)个矩形的并集覆盖的总面积。

数据范围:

  • \(1\leq n\leq 10^5,0\leq x_1,x_2,y_1,y_2\leq 10^9\)

思路:

  • 线段树求矩形面积并

  • 考虑这样一个两个矩形相交,划分一下面积,有:

  • 这样原先的图形就被分成了三份,每份图形的形状都规整(矩形)。

  • 也就是说,这时候用一条扫描线,从下往上扫,每次碰到一条横边就停下来,计算面积后累加至答案当中。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e5 + 10;
typedef long long ll;
int n;
ll x_axis[maxn<<1];

struct ScanLine
{
    ll l, r, h;
    int mark;
    bool operator < (const ScanLine b) const{
        return h < b.h;
    }
}line[maxn<<1];

struct SegmenTree
{
    int l, r;
    int sum; //被完全覆盖的次数
    ll len;  //区间内被截的长度
    #define lson p<<1
    #define rson p<<1|1
    #define l(x) tree[x].l
    #define r(x) tree[x].r
    #define sum(x) tree[x].sum
    #define len(x) tree[x].len
}tree[maxn<<2];

void build(int p, int l, int r)
{
    l(p) = l, r(p) = r;
    if(l == r) return;
    int mid = (l+r)>>1;
    build(lson, l, mid);
    build(rson, mid+1, r);
}

void pushup(int p)
{
    if(sum(p))
        len(p) = x_axis[r(p)+1]-x_axis[l(p)];
    else
        len(p) = len(lson) + len(rson);
}

void change(int p, ll l, ll r, int c)
{
    //l r表示需要修改的真实区间
    //l(p),r(p)表示线段树该节点管辖的下标范围
    if(x_axis[r(p)+1] <= l || r <= x_axis[l(p)])
        return; //不在维护的范围内
    //完全覆盖
    if(l <= x_axis[l(p)] && x_axis[r(p)+1] <= r)
    {
        sum(p) += c;
        pushup(p);
        return;
    }
    change(lson, l, r, c);
    change(rson, l, r, c);
    pushup(p);
}

int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
    {
        ll x1, x2, y1, y2;
        scanf("%lld%lld%lld%lld", &x1, &y1, &x2, &y2);
        x_axis[2*i-1] = x1, x_axis[2*i] = x2;
        line[2*i-1] = {x1, x2, y1, 1}; //左下角
        line[2*i]   = {x1, x2, y2, -1}; //右上角
    }
    n <<= 1;
    sort(line+1, line+1+n);
    sort(x_axis+1, x_axis+1+n);
    int tot = unique(x_axis+1, x_axis+1+n) - x_axis - 1;
    build(1, 1, tot-1); //tot个点中间有tot-1个区间

    ll ans = 0;
    for(int i = 1;  i < n; i++) //最后一条线不考虑
    {
        //将扫描线的信息导入线段树
        change(1, line[i].l, line[i].r, line[i].mark);
        //累加答案
        ans += tree[1].len*(line[i+1].h-line[i].h);
    }
    cout << ans << endl;
    return 0;
}
posted @ 2020-01-14 01:29  zhaoxiaoyun  阅读(398)  评论(0编辑  收藏  举报