Atlantis
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 15236 Accepted Submission(s): 6265
Problem Description
There are several ancient Greek texts that contain descriptions of the fabled island Atlantis. Some of these texts even include maps of parts of the island. But unfortunately, these maps describe different regions of Atlantis. Your friend Bill has to know the total area for which maps exist. You (unwisely) volunteered to write a program that calculates this quantity.
Input
The input file consists of several test cases. Each test case starts with a line containing a single integer n (1<=n<=100) of available maps. The n following lines describe one map each. Each of these lines contains four numbers x1;y1;x2;y2 (0<=x1<x2<=100000;0<=y1<y2<=100000), not necessarily integers. The values (x1; y1) and (x2;y2) are the coordinates of the top-left resp. bottom-right corner of the mapped area.
The input file is terminated by a line containing a single 0. Don’t process it.
The input file is terminated by a line containing a single 0. Don’t process it.
Output
For each test case, your program should output one section. The first line of each section must be “Test case #k”, where k is the number of the test case (starting with 1). The second one must be “Total explored area: a”, where a is the total explored area (i.e. the area of the union of all rectangles in this test case), printed exact to two digits to the right of the decimal point.
Output a blank line after each test case.
Output a blank line after each test case.
Sample Input
2
10 10 20 20
15 15 25 25.5
0
Sample Output
Test case #1
Total explored area: 180.00
Source
Recommend
linle
题目大意:求矩形并的面积.
分析:比较固定的解法:线段树+扫描线.矩形的面积公式是底乘高,每次把相同y坐标的底的长度给加起来,同时向上跳,直到遇到另外一条线位置,那么底的和*高就是扩展得到的面积,这样不断地扩展就能得到答案.具体算法可以参看:传送门
直接套线段树会有一个问题,[x,x]在这道题中表示的是一个点,是不合法的,而线段树中确是有意义的,如果处理到[x,x+1]继续分治成[x,x],[x+1,x+1]就会出现问题,正确的做法是每次将右端点往左移一位,特判如果此时的l=r,那么就是之前的[x,x+1]这种情况,直接判掉,否则就可以像线段树那样正常地递归下去.
这道题因为坐标比较大,需要离散化一下,因为高度是需要计算的,所以只需要离散横坐标就可以了.那么线段树记录的就是下标,每次修改一个值都必须要二分查找这个值对应的下标是哪一位.
比较坑的一个点是数据可能会有0,我在处理的时候没考虑到这种情况,一直爆栈,主要还是离散化的部分鲁棒性不强.
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 210; struct node { double l, r, h; int id; }e[maxn]; double X[maxn], c[maxn << 2], XX[maxn], ans; int cnt, n, tot, tag[maxn << 2], cas = 1; bool cmp(node a, node b) { return a.h < b.h; } int find(double x) { int l = 1, r = cnt, res = cnt - 1; while (l <= r) { int mid = (l + r) >> 1; if (X[mid] >= x) { res = mid; r = mid - 1; } else l = mid + 1; } return res; } void pushup(int o, int l, int r) { if (tag[o]) c[o] = X[r + 1] - X[l]; else if (l == r) //没有被完全覆盖就是两个点 c[o] = 0; else c[o] = c[o * 2] + c[o * 2 + 1]; } void update(int o, int l, int r, int x, int y, int v) { if (x <= l && r <= y) { tag[o] += v; pushup(o, l, r); return; } int mid = (l + r) >> 1; if (x <= mid) update(o * 2, l, mid, x, y, v); if (y > mid) update(o * 2 + 1, mid + 1, r, x, y, v); pushup(o, l, r); } int main() { while (~scanf("%d", &n) , n) { memset(c, 0, sizeof(c)); memset(tag, 0, sizeof(tag)); ans = 0.0; tot = cnt = 0; for (int i = 1; i <= n; i++) { double a, b, c, d; scanf("%lf%lf%lf%lf", &a, &b, &c, &d); e[++tot].l = a; e[tot].r = c; e[tot].h = b; e[tot].id = 1; X[tot] = a; e[++tot].l = a; e[tot].r = c; e[tot].h = d; e[tot].id = -1; X[tot] = c; } sort(X + 1, X + tot + 1); sort(e + 1, e + 1 + tot, cmp); XX[1] = X[1]; cnt = 1; for (int i = 2; i <= tot; i++) if (X[i] != X[i - 1]) XX[++cnt] = X[i]; memcpy(X, XX, sizeof(XX)); for (int i = 1; i < tot; i++) { int l = find(e[i].l), r = find(e[i].r) - 1; update(1, 1, cnt, l, r, e[i].id); ans += c[1] * (e[i + 1].h - e[i].h); } printf("Test case #%d Total explored area: %.2lf ", cas++, ans); } return 0; }