题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1542
题意: 给你n个矩形的左上角顶点与右下角顶点, 求 n个矩形的面积和(可能重叠,重叠的部分只算一次)。
思路: 扫描线模版题。关于扫描线建议看这个博客: http://www.cnblogs.com/scau20110726/archive/2013/04/12/3016765.html
还有一个比较重要的问题,
一般的线段树以及我们的区间修改合并,都有一个共同点,就是不会出现区间缺失的现象,什么叫区间缺失,顾名思义,区间缺失就是缺少一些区间没有进行运算,这里的扫描线就会遇到这个问题。
普遍的,我们的线段树以及数据区间分布是这样的:
[1, a][a + 1, b][b + 1, c][c + 1, d][d + 1, e].......
但是如果只是简简单单的用这个来解决扫描线的问题会导致错误,为什么因为,他没有涉及到[a,a + 1],在扫描线中会出现[a,a + 1]中的数据,而常用的线段树的区间概念是无法解决这样的问题的,出现了所谓的区间缺失,怎样解决,下面的代码给出了解决方案,这里简单的提一下,就是利用[ , ),这个区间性质,左闭右开,即可解决区间缺失问题
AC代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 210;
struct xx{ ///扫描线
double l,r,h; ///左端点,右端点与高度
int d; ///标号1或 -1,表明是下边还是上边
xx(){}
xx(double l,double r,double h,int d):l(l),r(r),h(h),d(d){}
}line[maxn << 4];
double sum[maxn << 4]; ///存放投影
double x[maxn << 4]; ///存放所有的x坐标
int cnt[maxn << 4]; ///cnt[i] != 0 说明 i代表的 这个区间 被覆盖, == 0则未被覆盖或未被完全覆盖
///(对于一个区间上下边一定是成对出现,扫描线从下往上扫,途中cnt的值可能大于1,但一定不可能小于0)
bool cmp(xx A, xx B){
return A.h < B.h;
}
void pushup(int l,int r,int i){
if(cnt[i]) sum[i] = x[r+1] - x[l]; /// cnt != 0 被覆盖,计算出这一段的长度, [ ) 避免区间缺失
else if(l == r) sum[i] = 0; /// 显然, 此时长度为 0
else sum[i] = sum[i<<1] + sum[i<<1|1]; /// 未被完全覆盖,(最后是push up 到sum[1]),所以 = 左右儿子的sum 之和
}
void build(int l,int r,int i){ /// 此建树操作 可用两个memset 代替
int mid = (l + r) >> 1;
sum[i] = 0;
cnt[i] = 0;
if(l == r) return ;
build(l,mid,i << 1);
build(mid + 1,r,i << 1 | 1);
// pushup(l,r,i);
}
void update(int l,int r,int ul,int ur,int i,int key){
if(ul <= l && r <= ur){ ///当前到达的区间 已经被包含在需要更新的区间中, 直接更新cnt , 然后pushup
cnt[i] += key;
pushup(l,r,i);
}else {
int mid = (l + r) >> 1;
if(ul <= mid) update(l,mid,ul,ur,i << 1,key);
if(mid < ur) update(mid + 1,r,ul,ur,i << 1 | 1,key);
pushup(l,r,i);
}
}
int main()
{
int n,k = 1;
while(~scanf("%d",&n)){
if(!n) break;
int pl = 1;
double x1,y1,x2,y2;
for(int i = 1;i <= n;i ++){
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
line[pl] = xx(x1,x2,y1,1);
x[pl++] = x1;
line[pl] = xx(x1,x2,y2,-1);
x[pl++] = x2;
}
///离散化
sort(line + 1,line + pl,cmp);
sort(x + 1,x + pl);
int lisan = unique(x + 1,x + pl) - x - 1;
build(1,lisan, 1);
double ans = 0;
for(int i = 1;i < pl;i ++){
int l = lower_bound(x+1,x+lisan+1,line[i].l) - x;
int r = lower_bound(x+1,x+lisan+1,line[i].r) - x - 1;///[ ) 的处理,注意这里的r有一个减一操作,这就是上面r+1的原因,保证区间左闭右开,这样可以防止区间缺失
update(1,lisan,l,r,1,line[i].d);
ans += sum[1] * (line[i+1].h - line[i].h);
}
printf("Test case #%d\n",k++);
printf("Total explored area: %.2lf\n\n",ans);
}
return 0;
}