题目链接:http://poj.org/problem?id=1151
题目大意:给你一些矩形,其顶点坐标是浮点数,可能相互重叠,问这些矩形覆盖到的面积是多少。
分析:我们将Y轴进行离散化,n个矩形的2n条边界,最多可将Y轴分成2n-1个区域,我们对这些区间进行编号,建立线段树。
const int M=210; struct segment { int l,r; double len; //表示区间上有多长的部分是落在矩形中的 int cov; //该区间当前被多少个矩形完全包含 int mid() { return (l+r)>>1; } }tree[M<<2];
将矩形的纵边从左到右排序,然后依次将这些纵边插入线段树,要记住哪些纵边是矩形的左边界(始边),哪些是右边界(终边),以便插入时,对len和cov做不同的修改。插入一条边后,就根据根结点的len值增加总覆盖面积area的值:area+=len*(该边到下一条边的距离)。
实现代码如下:
#include <cstdio> #include <algorithm> #include <iostream> using namespace std; const int M=210; struct segment { int l,r; double len; //表示区间上有多长的部分是落在矩形中的 int cov; //该区间当前被多少个矩形完全包含 int mid() { return (l+r)>>1; } }tree[M<<2]; struct Line { double x,y1,y2; bool bleft; //标记该线段是否为矩形的左边 bool operator <(const Line &a) const { return x<a.x; } }line[M]; double y[M]; //记录所有矩形的y坐标 void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; tree[rt].len=0; tree[rt].cov=0; if(l==r) return ; int m=tree[rt].mid(); build(rt<<1,l,m); build(rt<<1|1,m+1,r); } void update(int rt,int l,int r,bool op) { if(tree[rt].l==l&&tree[rt].r==r) { if(op) {//当前边是矩形的左边界的话,在该区间插入矩形左边的一部分或者全部,覆盖区间[l,r] tree[rt].len=y[r+1]-y[l]; tree[rt].cov++; } else { tree[rt].cov--; if(tree[rt].cov==0) { if(tree[rt].l==tree[rt].r) tree[rt].len=0; else tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len; } } return ; } int m=tree[rt].mid(); if(r<=m) update(rt<<1,l,r,op); else if(l>m) update(rt<<1|1,l,r,op); else { update(rt<<1,l,m,op); update(rt<<1|1,m+1,r,op); } if(tree[rt].cov==0) //如果不为0,说明该区间当前仍然被某个矩形完全包含,此时不更新len值 tree[rt].len=tree[rt<<1].len+tree[rt<<1|1].len; } int main() { int n,T=1; while(scanf("%d",&n)&&n) { int yc=0,lc=0; //y[]和line[]的计数器 for(int i=0;i<n;i++) { double x1,y1,x2,y2; scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2); y[yc++]=y1; y[yc++]=y2; line[lc].x=x1; line[lc].y1=y1; line[lc].y2=y2; line[lc].bleft=true; lc++; line[lc].x=x2; line[lc].y1=y1; line[lc].y2=y2; line[lc].bleft=false; lc++; } sort(y,y+yc); yc=unique(y,y+yc)-y; //排序并去重 build(1,0,yc); sort(line,line+lc); double area=0; for(int i=0;i<lc-1;i++) //插入每条边 { int l=lower_bound(y,y+yc,line[i].y1)-y; //二分找出当前边离散化后对应的线段树的区间端点 int r=lower_bound(y,y+yc,line[i].y2)-y; update(1,l,r-1,line[i].bleft); area+=tree[1].len*(line[i+1].x-line[i].x); } printf("Test case #%d\nTotal explored area: %.2f\n\n",T++,area); } return 0; }