题目链接:
https://vjudge.net/problem/HDU-1542
大牛博客链接:http://blog.csdn.net/u013480600/article/details/22548393
讲解的很生动。
分析:
首先我们将矩形的上下边分为上位边(即y坐标大的那条平行于x轴的边),和下位边(y坐标小的平行于x轴的边).然后我们把所有矩形的上下位边按照他们y坐标从小到大排序;
需要把x坐标离散化,这样才能用线段树来维护信息.所谓离散化,就是将元素排序,去重,这样得到一个数组a,这个数组就是建立线段树的关键;
主要知识:
关于线段树:线段树的叶节点([l,r],这里l==r,指的是建立线段树数组中的下标),线段树中叶节点代表的都是某一个数组的下标,其他节点则是多个下标。
建立一个线段树必须要有一个数组,根据其坐标建立线段树。本题中叶节点控制的[l,l]实际上代表的是[ a[l],a[l+1] ].线段树中其他节点控制的区间[L,R],也是指的x坐标轴的第L个区间到第R个区间的范围,也就是X[L]到X[R+1]坐标的范围.
关于离散化:
离散化就是压缩区间,使原有的长区间映射到新的短区间,但是区间压缩前后的覆盖关系不变,本题中离散化就是把矩形端点的x坐标放进一个数组中,去掉重复的坐标。,这就用到了unique函数。
关于unique函数:
unique()函数是一个去重函数,STL中unique的函数 unique的功能是去除相邻的重复元素(只保留一个),还有一个容易忽视的特性是它并不真正把重复的元素删除。之所以说比不真正把重复的元素删除,其实是,该函数把重复的元素一到后面去了,然后依然保存到了原数组中,返回值是相互间不相同的个数,注意用这个函数之前需要排序。
代码如下:
#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<vector>
using namespace std;
//扫描线,也就是矩形的和x轴平行的那些边,记录高度和左右端点坐标,和
class Seg
{
public:
double x1,x2,h;
int flag;
Seg(double a=0,double b=0,double c=0,double d=0):x1(a),x2(b),h(c),flag(d){}
};
bool cmp(Seg p,Seg q)
{
return p.h<q.h||(p.h==q.h&&p.flag>q.flag);
}
const int maxn=100+5;
Seg seg[maxn<<1];
double cnt[maxn<<3],sum[maxn<<3];//cnt表示当前区间状态,0表示被覆盖,大于零则被扫描,sum代表区间长度
double x[maxn<<1];
//建树,
void build(int l,int r,int rt)
{
cnt[rt]=sum[rt]=0;
if(l==r)return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
//注意[l,r]间的长度是x[r+1]-x[l]
void push_up(int l,int r,int rt)
{
if(cnt[rt])sum[rt]=x[r+1]-x[l];
else if(l==r)sum[rt]=0;
else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
//更新,如果是上位边则加一,下位边减一
void update(int ll,int rr,int c,int l,int r,int rt)
{
if(ll<=l&&rr>=r)
{
cnt[rt]+=c;
push_up(l,r,rt);
return ;
}
int m=(l+r)>>1;
if(ll<=m)update(ll,rr,c,l,m,rt<<1);
if(rr>m)update(ll,rr,c,m+1,r,rt<<1|1);
push_up(l,r,rt);
}
int main()
{
int n;
int kase=0;
while(scanf("%d",&n)!=EOF&&n)
{
int nums=0,numx=0;
for(int i=0;i<n;i++)
{
double a,b,c,d;
scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
seg[nums++]=Seg(a,c,b,1);
seg[nums++]=Seg(a,c,d,-1);
x[numx++]=a;
x[numx++]=c;
}
build(0,numx-2,1);
sort(x,x+numx);
sort(seg,seg+nums,cmp);
numx=unique(x,x+numx)-x; //离散化
double res=0;
//让每一条扫描线扫描
for(int i=0;i<nums-1;i++)
{
int ll=lower_bound(x,x+numx,seg[i].x1)-x;
int rr=lower_bound(x,x+numx,seg[i].x2)-x;
update(ll,rr-1,seg[i].flag,0,numx-2,1);
res+=sum[1]*(seg[i+1].h-seg[i].h);
}
printf("Test case #%d\n",++kase);
printf("Total explored area: %.2lf\n\n",res);
}
}