过度种植

过度种植

农夫约翰购买了一台新机器,该机器能够在其农场的任何“轴向对齐”(即具有垂直和水平边)的矩形区域内种草。

不幸的是,这台机器有一天出了故障,并在 N 个不同的矩形区域内进行了种草工作,其中一些区域可能会有重叠。

给定机器工作的具体 N 个矩形区域,请你计算种上草的区域的总面积是多少。

输入格式

第一行包含整数 N

接下来 N 行,每行包含四个整数 x1,y1,x2,y2,表示其中一个矩形区域的左上角坐标 (x1,y1) 和右下角坐标 (x2,y2)

输出格式

输出种上草的区域的总面积。

数据范围

1N10,
10000x1,y1,x2,y210000,
x1<x2
y1>y2

输入样例:

2
0 5 4 1
2 4 6 2

输出样例:

20

 

解题思路

  如果N比较大的话,例如N=1000,就需要用到扫描线。如果N=10000还需要用到线段树来优化。但这里的N最大为10,并不需要用到那么复杂的写法。容易发现这些矩形的并集是一个不规则的图形,比较难求,但这些矩形的交集也是矩形,就比较好求了。因此我们发现并集不好求而交集好求,就可以用到容斥原理。容斥原理的公式如下:|1inSi|=1in|Si|1i<jn|SiSj|+1i<j<kn|SiSjSk|+(1)n1|1inSi|

  这样就可以把求并集的问题转换为求交集的问题。虽然交集好求,但项数很多,一共有Cn1+Cn2++Cnn=2n1项,由于N最大只有10,因此最多也就只有1023项,再加上求所有矩形的交集,因此时间复杂度为O(2n×n)

  矩形求交集其实就是区间求交,我们把X轴和Y轴独立开来看,例如X轴上有两个区间[a,b][c,d],那么这两个区间的交集就是[max{a,c}, min{b,d}]。同理Y轴的区间求交也是如此。

  我们在枚举每次求交集的矩形个数的时候(也就是枚举Cni),可以用二进制枚举。同时读入的坐标是数学坐标系,y1>y2,为了让y1<y2x1<x2保持一致方便区间求交,我们把坐标系变成矩阵坐标系,即把左上角的点和右下角的点转化成左下角的点和右上角的点。

  AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 15, INF = 0x3f3f3f3f;
 5 
 6 int n;
 7 struct Node {
 8     int x1, y1, x2, y2;
 9 }p[N];
10 
11 int get(int st) {
12     // 初始定义一个无穷大的矩形
13     int x1 = -INF, y1 = -INF, x2 = INF, y2 = INF, cnt = 0;
14     for (int i = 0; i < n; i++) {
15         if (st >> i & 1) {
16             cnt++;  // 求交矩阵个数+1
17             
18             // x轴上求交
19             x1 = max(x1, p[i].x1);
20             x2 = min(x2, p[i].x2);
21             
22             // y轴上求交
23             y1 = max(y1, p[i].y1);
24             y2 = min(y2, p[i].y2);
25         }
26     }
27     
28     int ret = max(x2 - x1, 0) * max(y2 - y1, 0);    // 有可能没有交集,算出是负数,因此要与0取最大值
29     if (cnt % 2 == 0) ret *= -1;    // 如果矩形的个数为偶数,根据公式要返回负数值
30     
31     return ret;
32 }
33 
34 int main() {
35     scanf("%d", &n);
36     for (int i = 0; i < n; i++) {
37         scanf("%d %d %d %d", &p[i].x1, &p[i].y2, &p[i].x2, &p[i].y1);   // y1 < y2
38     }
39     
40     int ret = 0;
41     for (int i = 1; i < 1 << n; i++) {
42         ret += get(i);  // 获取i状态下(i的二进制有多少个1)的矩形求交的面积
43     }
44     printf("%d", ret);
45     
46     return 0;
47 }
复制代码

  补充个扫描线加线段树的做法。AC代码如下,时间复杂度为O(n×logM)

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 30, M = 2e4 + 10;
 5 
 6 struct Seg {
 7     int x, y1, y2, c;
 8 }seg[N];
 9 struct Node {
10     int l, r, cnt, len;
11 }tr[M * 4];
12 
13 void build(int u, int l, int r) {
14     if (l == r) {
15         tr[u] = {l, r, 0, 0};
16     }
17     else {
18         int mid = l + r >> 1;
19         build(u << 1, l, mid);
20         build(u << 1 | 1, mid + 1, r);
21         tr[u] = {l, r, 0, 0};
22     }
23 }
24 
25 void pushup(int u) {
26     if (tr[u].cnt) tr[u].len = tr[u].r + 1 - tr[u].l;
27     else if (tr[u].l == tr[u].r) tr[u].len = 0;
28     else tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
29 }
30 
31 void modify(int u, int l, int r, int c) {
32     if (tr[u].l >= l && tr[u].r <= r) {
33         tr[u].cnt += c;
34         pushup(u);
35     }
36     else {
37         int mid = tr[u].l + tr[u].r >> 1;
38         if (l <= mid) modify(u << 1, l, r, c);
39         if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
40         pushup(u);
41     }
42 }
43 
44 int main() {
45     int n;
46     scanf("%d", &n);
47     for (int i = 0, j = 0; i < n; i++) {
48         int x1, y1, x2, y2;
49         scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
50         seg[j++] = {x1, y2, y1, 1};
51         seg[j++] = {x2, y2, y1, -1};
52     }
53     sort(seg, seg + n + n, [&](Seg &a, Seg &b) {
54         return a.x < b.x;
55     });
56     build(1, -1e4, 1e4);
57     int ret = 0;
58     modify(1, seg[0].y1, seg[0].y2 - 1, seg[0].c);
59     for (int i = 1; i < n + n; i++) {
60         ret += tr[1].len * (seg[i].x - seg[i - 1].x);
61         modify(1, seg[i].y1, seg[i].y2 - 1, seg[i].c);
62     }
63     printf("%d", ret);
64     
65     return 0;
66 }
复制代码

 

参考资料

  AcWing 2032. 过度种植(春季每日一题2022):https://www.acwing.com/video/3878/

posted @   onlyblues  阅读(150)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示