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


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

    题意描述:

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

    输入格式:

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

    输出格式:

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

    数据范围:

    • (1leq nleq 10^5,0leq x_1,x_2,y_1,y_2leq 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;
    }
    
  • 相关阅读:
    C#中的反射解析及使用(转)
    给GRUB添加新的项目
    EFI系统引导的一些零碎知识点
    Mysql 通用知识 2019-03-27
    为git关联编辑器(比如notepad++) Associating text editors with Git
    win10 右键添加“在此打开powershell”
    LINQ
    Git学习笔记——分支
    Docker 安装 mysql
    RestTemplateBuilder类
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/12190124.html
Copyright © 2020-2023  润新知