[luogu p1884] [USACO12FEB]Overplanting S

Link\mathtt{Link}

传送门

Summarization\mathtt{Summarization}

在笛卡尔直角坐标系中,给定若干个四边均与坐标轴平行的矩形,求覆盖到的面积。

Solution\mathtt{Solution}

这个题目就是求所有矩形的面积并。

考虑这样一种矩形切割的做法:每到一个矩形 rr,就用 rr 来切割之前的所有矩形 aia_i,将 aia_i 切割为若干小矩形,加入 aa 数组,然后删除 aia_i 这个矩形,加入 rr 这个矩形。

什么意思呢,举一个例子吧。

我之前有一个矩形 a1a_1,现在来了一个矩形 rr

BwmFzQ.png

我们用 rr 切割 a1a_1 后的结果就是这样的:

BwamjI.png

可以看到,a1a_1 没有覆盖到的部分被切割成了两个矩形 a1a_1a2a_2,而重叠那部分人间蒸发了。而 rr 矩形形状不变,成为了 a3a_3

此时,一个新矩形 rr 也来凑热闹了。

BwucPf.png

这个 rr 会轮流查看自己能切到哪些矩形。a1,a3a_1, a_3 可以切到,a2a_2 不行。所以 rr 会轮流切 a1a_1a3a_3

先切 a1a_1

BwdJIO.png

再切 a3a_3,然后入队:

Bw09un.png

最后,计算每个矩形的面积即可。

思路明朗,代码实现就不难了。

矩形切割的cut函数如下:

void cut(int id, int x1, int y1, int x2, int y2) {
    long long k1, k2, k3, k4;
    k1 = max(a[id].x1, x1);
    k2 = min(a[id].x2, x2);
    k3 = min(a[id].y1, y1);
    k4 = max(a[id].y2, y2);
    if (a[id].x1 < k1) a[++cnt] = (rectangle){a[id].x1, a[id].y1, k1, a[id].y2};
    if (a[id].x2 > k2) a[++cnt] = (rectangle){k2, a[id].y1, a[id].x2, a[id].y2};
    if (a[id].y1 > k3) a[++cnt] = (rectangle){k1, a[id].y1, k2, k3};
    if (a[id].y2 < k4) a[++cnt] = (rectangle){k1, k4, k2, a[id].y2};
}

Code\mathtt{Code}

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-10-31 10:57:53 
 * @Last Modified by:   crab-in-the-northeast 
 * @Last Modified time: 2020-10-31 10:57:53 
 */
#include <iostream>
#include <cstdio>

const int maxn = 1005;
inline long long max(long long a, long long b) {
    return a > b ? a : b;   
}
inline long long min(long long a, long long b) {
    return a < b ? a : b;
}

struct rectangle {
    long long x1, y1;
    long long x2, y2;
}a[maxn];

int cnt = 0;

void cut(int id, int x1, int y1, int x2, int y2) {
    long long k1, k2, k3, k4;
    k1 = max(a[id].x1, x1);
    k2 = min(a[id].x2, x2);
    k3 = min(a[id].y1, y1);
    k4 = max(a[id].y2, y2);
    if (a[id].x1 < k1) a[++cnt] = (rectangle){a[id].x1, a[id].y1, k1, a[id].y2};
    if (a[id].x2 > k2) a[++cnt] = (rectangle){k2, a[id].y1, a[id].x2, a[id].y2};
    if (a[id].y1 > k3) a[++cnt] = (rectangle){k1, a[id].y1, k2, k3};
    if (a[id].y2 < k4) a[++cnt] = (rectangle){k1, k4, k2, a[id].y2};
}

int main() {
    int n;
    std :: scanf("%d", &n);
    
    long long x1, y1, x2, y2;
    std :: scanf("%lld %lld %lld %lld", &x1, &y1, &x2, &y2);
    a[++cnt] = (rectangle){x1, y1, x2, y2};
    for (int i = 2; i <= n; ++i) {
        std :: scanf("%lld %lld %lld %lld", &x1, &y1, &x2, &y2);
        for (int j = 1; j <= cnt; ++j) {
            if (a[j].x1 < x2 && a[j].x2 > x1 && a[j].y1 > y2 && a[j].y2 < y1) {
                cut(j, x1, y1, x2, y2);
                a[j--] = a[cnt--];
            }
        }
        a[++cnt] = (rectangle){x1, y1, x2, y2};
    }

    long long ans = 0;
    for (int i = 1; i <= cnt; ++i)
        ans += (a[i].x2 - a[i].x1) * (a[i].y1 - a[i].y2);
    std :: printf("%lld\n", ans);
    return 0;
}

More\mathtt{More}

如果使用容斥原理,一个地方可能重复不止两次,所以有可能会算乱。

为了避免这种情况,我们便采用矩形切割这种做法。

好做不乱。

据说本题还可以用线段树+离散化,但是想想就麻烦。。

posted @   dbxxx  阅读(292)  评论(1编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示