题意:给定n个矩形 (n<= 100),其顶点坐标是浮点数,可能互相重叠,问这些矩形覆盖到的面积是多大。
思路:线段树+扫描线。用一条直线从左到右扫描,碰到一条矩形竖边的时候,就计算该直线有多长被矩形覆盖,以及被覆盖部分是覆盖了几重。碰到矩形左边,要增加被覆盖的长度,碰到右边, 要减少被覆盖的长度。每碰到一条矩形的纵边,覆盖面积就增加 Len * 该纵边到下一条纵边的距离。Len是此时扫描线被矩形覆盖的长度。
在Y轴进行离散化。n个矩形的2n个横边纵 坐标共构成最多2n-1个区间的边界,对这些区间编号,建立起线段树。
注意灵活运用stl提供的函数。
unique的作用是从输入序列中“删除”所有相邻的重复元素,即去重。
lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置,在本题中即返回查找到的y数组下标。
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 100005 struct line{ //表示矩形的竖边 double x,y1,y2; int l; //表示是否为矩形的左边 bool operator<(const line &b) const{ return x<b.x; } }line[N<<1]; struct tree{ int l,r; int num; //覆盖的重数 double len; }t[N<<3]; double y[N<<1]; int ylen; int T = 1,n; int mid(int a,int b){ return (a+b)>>1; } void build(int r,int left,int right){ t[r].l = left; t[r].r = right; t[r].num = t[r].len = 0; if(left == right) return; build(r*2, left, mid(left, right)); build(r*2+1, mid(left, right)+1, right); } void insert(int r,int left,int right){ int mm = mid(t[r].l,t[r].r); if(t[r].l == left && t[r].r == right){ t[r].len = y[right]-y[left-1]; t[r].num++; return; } if(right<=mm) insert(r*2, left, right); else if(left>mm) insert(r*2+1, left, right); else{ insert(r*2, left, mm); insert(r*2+1, mm+1, right); } if(t[r].num == 0) t[r].len = t[r*2].len + t[r*2+1].len; } void del(int r,int left,int right){ int mm = mid(t[r].l, t[r].r); if(t[r].l == left && t[r].r == right){ t[r].num--; if(t[r].num == 0){ if(left == right) t[r].len = 0; else t[r].len = t[r*2].len+t[r*2+1].len; } return; } if(right <= mm) del(r*2,left,right); else if(left > mm) del(r*2+1,left,right); else{ del(r*2,left,mm); del(r*2+1,mm+1,right); } if(t[r].num == 0) t[r].len = t[r*2].len+t[r*2+1].len; } int main(){ while(scanf("%d",&n) && n){ int i,j,y1,y2; double a,b,c,d,res=0; ylen = 0; for(i = 0;i<n;i++){ scanf("%lf %lf %lf %lf",&a,&b,&c,&d); y[ylen++] = b; y[ylen++] = d; line[i*2].x = a; line[i*2].l = 1; line[i*2+1].l = 0; line[i*2+1].x = c; line[i*2].y1 = line[i*2+1].y1 = b; line[i*2].y2 = line[i*2+1].y2 = d; } sort(line,line+2*n); sort(y,y+ylen); ylen = unique(y, y+ylen)-y; build(1,1,ylen-1); //按照区间的个数建树 for(i = 0;i<2*n-1;i++){ y1 = lower_bound(y,y+ylen,line[i].y1)-y; y2 = lower_bound(y,y+ylen,line[i].y2)-y; if(line[i].l) //注意,y[0]~y[1]是区间1 insert(1,y1+1,y2); else del(1,y1+1,y2); res += t[1].len*(line[i+1].x - line[i].x); } printf("Test case #%d\n",T++); printf("Total explored area: %.2lf\n\n",res); } return 0; }