题目大意:给出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; }