过度种植
过度种植
农夫约翰购买了一台新机器,该机器能够在其农场的任何“轴向对齐”(即具有垂直和水平边)的矩形区域内种草。
不幸的是,这台机器有一天出了故障,并在 个不同的矩形区域内进行了种草工作,其中一些区域可能会有重叠。
给定机器工作的具体 个矩形区域,请你计算种上草的区域的总面积是多少。
输入格式
第一行包含整数 。
接下来 行,每行包含四个整数 ,表示其中一个矩形区域的左上角坐标 和右下角坐标 。
输出格式
输出种上草的区域的总面积。
数据范围
,
,
,
。
输入样例:
2 0 5 4 1 2 4 6 2
输出样例:
20
解题思路
如果比较大的话,例如,就需要用到扫描线。如果还需要用到线段树来优化。但这里的最大为,并不需要用到那么复杂的写法。容易发现这些矩形的并集是一个不规则的图形,比较难求,但这些矩形的交集也是矩形,就比较好求了。因此我们发现并集不好求而交集好求,就可以用到容斥原理。容斥原理的公式如下:
这样就可以把求并集的问题转换为求交集的问题。虽然交集好求,但项数很多,一共有项,由于最大只有,因此最多也就只有项,再加上求所有矩形的交集,因此时间复杂度为。
矩形求交集其实就是区间求交,我们把轴和轴独立开来看,例如轴上有两个区间和,那么这两个区间的交集就是。同理轴的区间求交也是如此。
我们在枚举每次求交集的矩形个数的时候(也就是枚举),可以用二进制枚举。同时读入的坐标是数学坐标系,,为了让与保持一致方便区间求交,我们把坐标系变成矩阵坐标系,即把左上角的点和右下角的点转化成左下角的点和右上角的点。
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代码如下,时间复杂度为:
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/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16300121.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效