H - 覆盖的面积(线段树-线段扫描 + 离散化(板题))

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
在这里插入图片描述
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.
注意:本题的输入数据较多,推荐使用scanf读入数据.
Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.

知识点简介

  • 线段树扫描可以横向扫描水平的边,也可纵向扫描竖直的边。
  • 这里我们们只讨论 扫描水平边的情况,我们先引入两个概念:上行边、下行边,上行边就是某个矩形上边的那条边,下行边就是某个举行下边的那条边。
  • 线段树扫描线段树的本质:就是将所有的给的矩形,把它们的水平线一一的抽出来,按水平线的高度从下到上一一添加边,在添加水平边的时候,我们要不断维护扫描线在x轴的有效长度,我们每添加一条下行边,那么这个我们就要在这个扫描线所在x轴上的区间的所有的sum[ ](这里的的sum其实是区间和)加1(如下图扫描线1:在(10,20)的区间的值都加1 ->sum[10,20]),如过每增加一条上行边(如:扫描线3),那么所有的相应的区间sum都减1,而这样通过这样不断的添加扫描线通过sum[1]来获取总的当前总的扫描线在x轴上的有效扫描面积,而两条扫描之间的高度差是很容易得到的,这样通 两条扫描的高度差 x 扫描线在x轴上的有效距离 就可求出两条扫描线之间的有效距离了。通过不断的添加扫描线并重复上述过程就可得到总的有效面积了
  • 线段树的有一个千万要注意的地方: 线段树的叶节点存储的是什么?? 如果不考虑离散化的话:叶节点存储的是 矩形的端点横坐标(排序后的),这样通过两个叶节点相减(横坐标相减),我们就能得到一个长度区间,而这个区间有可能是扫描线在x轴有效长度的一个组成部分,这样同多个相应的叶节点横坐标相减就能够得到 总的扫描的线的有效长度
  • 还有一个要注意的地方:就是当我代入右边界r到 Update() 函数的时候我们带的是 r - 1, 而在Push_up()函数中用到的又是 (r - 1) + 1,这个地方希望我们都仔细想想。。理解理解
    在这里插入图片描述
    dalao线段树讲解传送门
    接下来继续贴几张别人的图来帮助自己理解:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

代码一(扫描线)

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

using namespace std;

#define rtr rt << 1 | 1
#define rtl rt << 1

const int maxn = 10005;
struct Line
{
    double l, r, h;
    int d;
    bool operator < (const Line & a) const
    {
        return h < a.h;
    }
} line[maxn];

int mrk[maxn << 2];
double sum1[maxn << 2]; 
double sum2[maxn << 2];
double X[maxn];

void init()
{
    memset(mrk, 0, sizeof(mrk));
    memset(sum1,0, sizeof(sum1));
    memset(sum2,0, sizeof(sum2));
}

void Push_up(int rt, int l, int r)
{
    if(mrk[rt]) sum1[rt] = X[r + 1] - X[l];
    else if(l == r) sum1[rt] = 0;
    else sum1[rt] = sum1[rtl] + sum1[rtr];

    if(mrk[rt] >= 2) sum2[rt] = X[r + 1] - X[l];
    else if(l == r) sum2[rt] = 0;
    else if(mrk[rt] == 1) sum2[rt] = sum1[rtl] + sum1[rtr];
    else sum2[rt] = sum2[rtl] + sum2[rtr];
}

void Update(int rt, int l, int r, int s, int e, int d)
{
    if(s <= l && r <= e)
    {
        mrk[rt] += d;
        Push_up(rt ,l ,r);
        return;
    }

    int m = l + r >> 1;
    if(s <= m) Update(rtl, l, m, s, e, d);
    if(e >  m) Update(rtr, m+1, r, s, e, d);
    Push_up(rt, l, r);
}

int Sch(double val, double X[], int len)
{
    int l = 0, r = len - 1;
    while(l <= r)
    {
        int m = (l + r) >> 1;
        if(X[m] == val) return m;
        if(X[m] > val) r = m - 1;
        else l = m + 1;
    }
    return -1;
}

int main()
{
    //freopen("A.txt","r",stdin);
    int t;
    scanf("%d", &t);
    while(t --)
    {
        init();
        int n;
        scanf("%d", &n);
        double x1, y1, x2, y2;
        int a = 0;
        for(int i = 1; i <= n; i ++)
        {
            scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
            X[a] = x1;      
            line[a ++] = (Line){ x1, x2, y1, 1};
            X[a] = x2;
            line[a ++] = (Line){ x1, x2, y2, -1};
        }
        //排序
        sort(X, X + a);
        sort(line, line + a);
        //离散化
        int b = 1;
        for(int i = 1; i < a; i ++)
            if(X[i] != X[i - 1])
                X[b ++]  = X[i];
        
        //遍历讨论
        double ans = 0;
        for(int i = 0; i < a - 1; i ++)
        {
            int s = Sch(line[i].l, X, b);
            int e = Sch(line[i].r, X, b) - 1;
            Update(1, 0, b-1, s, e, line[i].d);
            ans += sum2[1] * (line[i+1].h - line[i].h);
        }
        printf("%.2lf\n", ans);
    }

    return 0;
}

代码二(暴力递归下方标记)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

#define db double
#define rtl rt << 1
#define rtr rt << 1 | 1
const int mxn = 10005;
struct Line
{
    db l, r, h;
    int d;
    bool operator < (const Line &a) const 
    {
        return h < a.h;
    }
} line[mxn];

struct    Node
{
    int cnt, lazy;
    db sum;
} tre[mxn << 2];

db X[mxn];


void Push_up(int rt, int l, int r)
{
    if(tre[rt].cnt >= 2) tre[rt].sum = X[r + 1] - X[l];
    else if(l == r) tre[rt].sum = 0;
    else tre[rt].sum = tre[rtl].sum + tre[rtr].sum;
}
void Push_down(int rt, int l, int r)
{
    if(! tre[rt].lazy) return;
    tre[rtl].cnt += tre[rt].lazy;
    tre[rtr].cnt += tre[rt].lazy;
    tre[rtl].lazy += tre[rt].lazy;
    tre[rtr].lazy += tre[rt].lazy;
    tre[rt].lazy = 0;
}

void Build(int rt, int l, int r)
{
    tre[rt].cnt = tre[rt].lazy = tre[rt].sum = 0;

    if(l == r) return;

    int m = (l + r) >> 1;
    Build(rtl, l, m);
    Build(rtr, m+1, r);
}

void Update(int rt, int l, int r, int s, int e, int d)
{
    if(s <= l && r <= e)
    {
        tre[rt].cnt += d;
        tre[rt].lazy += d;          //这个区间更新了,打一个标记
        Push_up(rt, l , r);         //更行这个区间是因为,父区间可能用得到
        return;
    }
    Push_down(rt, l, r);
    int m = (l + r) >> 1;
    if(s <= m) Update(rtl, l, m, s, e, d);
    if(e >  m) Update(rtr, m+1, r, s, e, d);
    Push_up(rt, l, r);
}

void Query(int rt, int l, int r, int s, int e)
{
    if(l == r)
    {
        Push_up(rt, l, r);
        return;
    }

    Push_down(rt, l, r);            //查询的时候将所有经过的层下放

    int m = (l + r) >> 1;
    if(s <= m)
        Query(rtl, l, m, s, e);
    if(e >  m)
        Query(rtr, m+1, r, s, e);
    Push_up(rt, l, r);
}

int Sch(db val, db X[], int len)
{
    int l = 0, r = len - 1;
    while(l <= r)
    {
        int m = (l + r) >> 1;
        if(X[m] == val) return m;
        if(X[m] > val) r = m - 1;
        else l = m + 1;
    }
    return -1;
}

int main()
{
    //freopen("A.txt","r",stdin);
    int t;
    scanf("%d", &t);
    while(t --)
    {
        int n;
        scanf("%d", &n);
        db x1, y1, x2, y2;
        int a = 0;
        for(int i = 1; i <= n; i ++)
        {
            scanf("%lf %lf %lf %lf", &x1, &y1, &x2, &y2);
            X[a] = x1;
            line[a ++] = (Line){ x1, x2, y1, 1 };
            X[a] = x2;
            line[a ++] = (Line){ x1, x2, y2, -1 };
        }

        sort(X, X + a);
        sort(line, line + a);

        int b = 1;
        for(int i = 1; i < a ; i ++)
            if(X[i] != X[i - 1])
                X[b ++] = X[i];

        Build(1, 0, b - 1);
        db ans = 0;
        for(int i = 0; i < a - 1; i ++)
        {
            int s = Sch(line[i].l, X, b);
            int e = Sch(line[i].r, X, b) - 1;
            Update(1, 0, b-1, s, e, line[i].d);
            Query(1, 0, b-1, s, e);
            ans += tre[1].sum * (line[i + 1].h - line[i].h);
        }
        printf("%.2lf\n", ans);
    }
    return 0;
}
发布了136 篇原创文章 · 获赞 218 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34261446/article/details/104905933