Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/32768 K (Java/Others)
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.
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.
Sample Input
2 10 10 20 20 15 15 25 25.5 0
Sample Output
Test case #1 Total explored area: 180.00
参考:https://blog.csdn.net/tomorrowtodie/article/details/52048323
区间扫描求矩形覆盖的面积,先按照纵坐标将图形分成几部分,如图所示
这样,可以看作是一条水平线从下到上扫过所有的水平边。对于面积的计算可以表示为水平线上的长度和两条水平线的高度差的乘积。其中高度差是很好求的,但是每个水平上的长度却不好解决。
可以这样,把每个矩形的X坐标进行保存,然后构建一棵线段树,维护X区间上每两个点的距离(差)。将一个矩形的左右两个横坐标在X中的位置添加到线段树中进行区间维护,这样线段树的根节点的值就是该条水平线上的长度。如果一个矩形的边是下面的边,就把这条边的两个坐标加到区间中,如果是上面的边的话就把它从线段树上去掉。
#include "bits/stdc++.h" #define lson o<<1, l, mid #define rson o<<1|1, mid+1, r #define ll o<<1 #define rr o<<1|1 using namespace std; const int maxn = 4000 + 10; struct Line { double l, r, h;//每条线的左右横坐标 纵坐标 int f;//标明边是上边还是下边 } edge[maxn]; struct Node { int l, r, s;//区间左端点 右端点 是否被访问 double len;//区间长度 } q[maxn*10]; int N; double X[maxn]; bool cmp(Line a, Line b) {return a.h < b.h;} void push_up(int o) { //线段在区间的时候 且为添加线段 if (q[o].s) q[o].len = X[q[o].r+1]-X[q[o].l]; //如果删除的是一个叶子节点 else if (q[o].l == q[o].r) q[o].len = 0; //删除节点 或者更新的区间不是一个整区间 else q[o].len = q[ll].len + q[rr].len; } void build_tree(int o, int l, int r) { q[o].l = l, q[o].r = r; q[o].s = 0, q[o].len = 0; if (l == r) return; int mid = (l+r)>>1; build_tree(lson); build_tree(rson); } void updata_tree(int o, int l, int r, int val) { if (q[o].l == l and q[o].r == r) { q[o].s += val;//上边从线段树的维护中删除 下边进入 push_up(o); return; } int mid = (q[o].l+q[o].r) >> 1; if (r <= mid) updata_tree(ll, l, r, val); else if (l > mid) updata_tree(rr, l, r, val); else { updata_tree(lson, val); updata_tree(rson, val); } push_up(o); } int main(int argc, char const *argv[]) { int Kcase = 0; while (scanf("%d", &N), N) { double res = 0.0; int tot = 0; for (int i = 0; i < N; i++) { double x1, x2, y1, y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2); edge[tot].l = x1, edge[tot].r = x2; edge[tot].h = y1; edge[tot].f = 1; X[tot] = x1; edge[++tot].l = x1, edge[tot].r = x2; edge[tot].h = y2; edge[tot].f = -1; X[tot] = x2; tot++; } sort(edge, edge+tot, cmp); sort(X, X + tot); int k = 1; //去重 for (int i = 1; i < tot; i++) if (X[i] != X[i-1]) X[k++] = X[i]; build_tree(1, 0, k-1); for (int i = 0; i < tot; i++) { int l = lower_bound(X, X+k, edge[i].l) - X; int r = lower_bound(X, X+k, edge[i].r)-X-1; updata_tree(1, l, r, edge[i].f); res += (edge[i+1].h-edge[i].h)*q[1].len; } printf("Test case #%d Total explored area: %.2f ", ++Kcase, res); } return 0; }