覆盖的面积
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3534 Accepted Submission(s): 1731
Problem Description
给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.
Input
输入数据的第一行是一个正整数T(1<=T<=100),代表测试数据的数量.每个测试数据的第一行是一个正整数N(1<=N<=1000),代表矩形的数量,然后是N行数据,每一行包含四个浮点数,代表平面上的一个矩形的左上角坐标和右下角坐标,矩形的上下边和X轴平行,左右边和Y轴平行.坐标的范围从0到100000.
注意:本题的输入数据较多,推荐使用scanf读入数据.
注意:本题的输入数据较多,推荐使用scanf读入数据.
Output
对于每组测试数据,请计算出被这些矩形覆盖过至少两次的区域的面积.结果保留两位小数.
Sample Input
2
5
1 1 4 2
1 3 3 7
2 1.5 5 4.5
3.5 1.25 7.5 4
6 3 10 7
3
0 0 1 1
1 0 2 1
2 0 3 1
Sample Output
7.63
0.00
思路:
线段树 + 扫描线 + 离散化。len 维护该区间内已被覆盖的长度,one 维护这个区间内覆盖一次的长度,two 维护这个区间内被覆盖两次或者两次以上的长度。每个区间覆盖的长度 len 要始终等于 one + two。主要是 push_up 的不同:
void push_up (int node, int l, int r) { if (cover[node] >= 2) { len[node] = two[node] = yy[r] - yy[l]; one[node] = 0; //当覆盖两次,则 one 应该为0 } else if (cover[node] == 1) { len[node] = yy[r] - yy[l]; two[node] = two[node << 1] + two[node << 1 | 1]; two[node] += (one[node << 1] + one[node << 1 | 1]); one[node] = len[node] - two[node]; //当覆盖一次,two 应该为左右儿子 two 之和,加上它这层在原来左右儿子 one 上的和 //总长减去 two 的长度就是 one 的长度, } else if (r - l == 1) { len[node] = two[node] = one[node] = 0; //未被覆盖且是叶子节点则为 0 } else { len[node] = len[node << 1] + len[node << 1 | 1]; two[node] = two[node << 1] + two[node << 1 | 1]; one[node] = one[node << 1] + one[node << 1 | 1]; //未被覆盖且不是叶子节点则全为左右儿子各部分之和 } }
AC:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX = 2050 * 2 * 3; typedef struct { double x, y1, y2; int temp; }node; node line[2050]; double yy[2050 * 2]; double one[MAX], two[MAX], len[MAX]; int cover[MAX]; bool cmp (node a, node b) { if (a.x != b.x) return a.x < b.x; return a.temp > b.temp; } void push_up (int node, int l, int r) { if (cover[node] >= 2) { len[node] = two[node] = yy[r] - yy[l]; one[node] = 0; } else if (cover[node] == 1) { len[node] = yy[r] - yy[l]; two[node] = two[node << 1] + two[node << 1 | 1]; two[node] += (one[node << 1] + one[node << 1 | 1]); one[node] = len[node] - two[node]; } else if (r - l == 1) { len[node] = two[node] = one[node] = 0; } else { len[node] = len[node << 1] + len[node << 1 | 1]; two[node] = two[node << 1] + two[node << 1 | 1]; one[node] = one[node << 1] + one[node << 1 | 1]; } } void build (int node, int l, int r) { if (r - l == 1) { cover[node] = len[node] = 0; two[node] = one[node] = 0; } else { int mid = (r + l) >> 1; build(node << 1, l, mid); build(node << 1 | 1, mid, r); push_up(node, l, r); } } void updata (int node, int l, int r, int cl, int cr, int c) { if (cl > r || cr < l) return; if (cl <= l && cr >= r) { cover[node] += c; push_up(node, l, r); return; } if(r - l == 1) return; int mid = (r + l) >> 1; if (l <= mid) updata(node << 1, l, mid, cl, cr, c); if (r >= mid) updata(node << 1 | 1, mid, r, cl, cr, c); push_up(node, l, r); } int main() { int t; scanf("%d", &t); while (t--) { int n; scanf("%d", &n); int ans = 0, m = 0; while (n--) { double x1, y1, x2, y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); line[ans].x = x1; line[ans].y1 = y1; line[ans].y2 = y2; line[ans++].temp = 1; line[ans].x = x2; line[ans].y1 = y1; line[ans].y2 = y2; line[ans++].temp = -1; yy[m++] = y1; yy[m++] = y2; } sort(yy, yy + m); m = unique(yy, yy + m) - yy; sort(line, line + ans, cmp); build(1, 0, m - 1); double res = 0; for (int i = 0; i < ans; ++i) { int ll = lower_bound(yy, yy + m, line[i].y1) - yy; int rr = lower_bound(yy, yy + m, line[i].y2) - yy; if(i) res += two[1] * (line[i].x - line[i - 1].x); updata(1, 0, m - 1, ll, rr, line[i].temp); } printf("%.2lf\n", res); } return 0; }
相关推荐
ACM中对于矩形面积并用线段树+离散化+ 扫描线一类问题求解
离散化是处理大规模数据时的一个技巧,它可以将数据映射到一个较小的范围内,从而减少线段树的空间需求,并可能加快查询和更新的效率。 扫描线技术是处理与线段树相关的一些问题时使用的,它用于动态处理线段树中的...
- Atlantis(1151):经典的扫描线求矩形面积交,通过离散化和线段树实现。 - Picture(1177):要求求解矩形周长的并,需要维护线段树中的多个信息。 - K-th Number(2155):使用线段树维护归并排序树,并采用...
- **POJ 1151**:结合了线段树和离散化的技巧来求解矩形面积的并集。这需要对输入的坐标进行离散化处理,以减少空间复杂度,并使用线段树维护区间信息。 - **POJ 1177**:求解矩形周长的并集。此题与POJ 1151类似,...
离散化+线段树 灯光投影 网络赛取连续子序列问题 线段树+树状数组+并查集,转化为排队问题 离散化 离散化矩形切割,矩形覆盖面积统计 覆盖矩形周长统计 离散化矩形切割 灯光投影 搜索 导弹 Bfs+hash状态的抽象...
总结起来,这两个问题都需要高效的算法和数据结构来处理大规模数据,如扫描线、线段树和单调队列,以及离散化技术来优化处理时间。这些问题不仅出现在NOIP这样的编程竞赛中,也在实际的软件开发和数据分析中有着广泛...
离散化线段树扫描线 离散化线段树结合扫描线算法,能够有效地处理动态区间问题,例如区间加减、查询等,特别适合于处理连续区间上的操作。 ### 7. 数论 在编程竞赛中,数论技巧广泛应用于素数判断、模逆运算、中国...
在Visual C++环境中,开发者可以利用STL(Standard Template Library)中的容器和算法来实现线段树和离散化,同时还可以利用MFC(Microsoft Foundation Classes)或者ATL(Active Template Library)等库来方便地与...
4.扫描线 (矩形覆盖求面积) (矩形覆盖求周长) 5.凸包 (平面上最远点对) 6.求凸多边形的直径 7.求凸多边形的宽度 8.求凸多边形的最小面积外接矩形 9.半平面交 图论: 基础:前向星 1.最短路(优先队列dijkstra) 2.判断...
8. 线段树:基本操作和应用 9. 并查集:应用和高级用法 10. 判断点在多边形内、差分约束系统 数据结构部分 1. 树状数组:基本概念和应用 2. 并查集:基本概念和应用 3. 堆排序:基本概念和应用 4. 字符串匹配:KMP...
21. 算法优化:包括快速幂优化、矩阵快速幂、树状数组的常用操作、扫描线技术等。 22. 算法模板和编程技巧:例如快速幂的模实现、n的阶乘的斯特林公式和欧拉公式、素数筛法的实现、线性同余方程组的中国剩余定理通...
2.19计算两条共线的线段的交点 2.20计算线段或直线与线段的交点 2.21求线段或直线与圆的交点 2.22中心点的计算 2.23过点作垂线 2.24作平行线 2.25过点作平行线 2.26线段延长 2.27三点画圆 2.28线段打断 ...
4. **计算几何中的算法**:如坐标离散化、扫描线算法和半平面交等,用于处理几何对象的计算问题。 **C++标准模板库的应用**: C++的STL(Standard Template Library)包含容器、迭代器、算法和函数对象,为编程...
对于填充操作,可能涉及到扫描线算法或者八叉树填充等技术。 图形用户界面(GUI)也是这个程序的一部分,可能使用了事件驱动编程模型,监听用户的点击和拖动操作,以动态改变圆心位置或半径。这涉及到了图形库的...
- **Z 缓存三角形的扫描线深度梯度**: 介绍了一种计算Z缓存中三角形扫描线深度变化的方法。 - **模拟雾和霾**: 描述了如何在计算机图形学中模拟雾和霾的效果。 - **纹理贴图索引的解释**: 讨论了如何正确理解和使用...
- **计算几何学**:包括**坐标离散化**、**扫描线算法**、**多边形的内核**和**几何工具的综合应用**。 最后,对于更高级的算法挑战,你可以学习: - **度限制最小生成树**和**第K最短路**。 - **最小树形图**和**...
- **离散化与扫描**:通过离散化技术简化问题,并使用扫描线算法求解。 #### 数据结构 - **广度优先搜索**:一种图遍历算法,按层次顺序访问每个顶点。 - **验证括号匹配**:检查括号序列是否正确配对。 - **表达式...
11. **多边形填充**:扫描线算法避免重复访问像素,但维护和排序代价高;边填充算法适合帧缓冲系统,但可能导致重复访问;边标志算法不能彻底解决重复访问问题。 12. **插值与逼近**:插值是确保函数经过所有给定点...
5. **裁剪**:是图形处理中的一个步骤,目的是去除不在视窗内的部分,保留可视部分,例如窗口裁剪、扫描线裁剪等。 6. **扫描转换算法**:用于将多边形转换成像素的算法,如六边形的填充,这里提到了使用活跃边表的...
首先对顶点按极角顺序排序,然后通过扫描线方法剔除内点,最终得到凸包上的顶点序列。 5. **最长递增子序列(LIS)**:在给定序列中寻找最长的严格递增子序列。可以采用动态规划或二分查找优化的贪心策略来求解。 ...