题目大意:给出n个矩形,求面积并
分析:线段树+离散化+扫描线。
先以Y轴(横线)建立线段树,当然,这里需要离散化。
然后,用一条直线从左到右扫描,碰到一条矩形竖边的时候,就计算该直线有多长被矩形覆盖,以及被覆盖部分是覆盖了几重。碰到矩形左边,要增加被覆盖的长度,碰到右边,要减少被覆盖的长度随着扫描线的右移动,覆盖面积不断增加。每碰到一条矩形的纵边,覆盖面积就增加( 此时扫描线被矩形覆盖的长度* 该纵边到下一条纵边的距离)。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct Seg{
double x, y1, y2;
bool left; //是否是矩形的左边
Seg(){}
Seg(double x, double y1, double y2, bool left):x(x), y1(y1), y2(y2) ,left(left){}
bool operator < (const Seg & s1) const {
return x < s1.x;
}
}seg[210];
int n;
double y[210];
double len[1000]; //当前区间覆盖长度
int cover[1000]; //当前区间被几个矩形覆盖
int yc, lc;
void PushUp(int root) {
if(cover[root] == 0) 如果不为0,则说明当前区间仍然被某个矩形完全覆盖,则不能更新len
len[root] = len[2*root+1]+len[2*root+2];
return;
}
void Insert(int root, int l, int r, int L, int R) {
if(L <= l && r <= R) {
len[root] = y[r+1] - y[l]; //区间编号加1,就是横线的位置
cover[root]++;
return;
}
int m = (l+r)/2;
if(L <= m)
Insert(2*root+1, l, m, L, R);
if(R > m)
Insert(2*root+2, m+1, r, L, R);
PushUp(root);
return;
}
void Delete(int root, int l, int r, int L, int R) {
if(L <= l && r <= R) {
cover[root]--;
if(cover[root] == 0) {
if(l == r)
len[root] = 0;
else
PushUp(root);
}
return;
}
int m = (l+r)/2;
if(L <= m)
Delete(2*root+1, l, m, L, R);
if(R > m)
Delete(2*root+2, m+1, r, L, R);
PushUp(root);
return;
}
int Bin(double v) { //在区间[l,r)中查找v,找不到就返回 yc,若返回的是yc,则说明已经是最后一条直线了
int l = 0, r = yc-1;
while(l <= r) {
int m = (l+r)/2;
if(y[m] == v) return m;
else if(y[m] > v) r = m-1;
else l = m+1;
}
return yc;
}
void Build(int root, int l, int r) {
len[root] = cover[root] = 0;
if(l == r) return;
Build(2*root+1, l, (l+r)/2);
Build(2*root+2, (l+r)/2+1, r);
return;
}
int main() {
int t = 0;
while(scanf("%d", &n) && n) {
double area = 0;
yc = 0, lc = 0;
while(n--) {
double x1, y1, x2, y2;
scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
y[yc++] = y1, y[yc++] = y2;
seg[lc++] = Seg(x1, y1, y2, true);
seg[lc++] = Seg(x2, y1, y2, false);
}
sort(y, y+yc);
sort(seg, seg+lc);
yc = unique(y, y+yc)-y;
Build(0, 0, yc-1-1);
for(int i = 0; i < lc-1; i++) {
int L = Bin(seg[i].y1);
int R = Bin(seg[i].y2)-1; //树上的一个点其实是表示一个区间,所以,得出的位置减1就是区间编号
if(seg[i].left)
Insert(0, 0, yc-2, L, R);
else
Delete(0, 0, yc-2, L, R);
area += len[0]*(seg[i+1].x-seg[i].x);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n", ++t, area);
}
return 0;
}