题目来源:http://poj.org/problem?id=1151
题意
在二维坐标系上,给出多个矩形的左下以及右上坐标,求出所有矩形构成的图形的面积。
思路
第一次接触扫描线。。。(弱)
好吧,我承认我会了。。。
咳咳,,推荐另外一道题(进行扫描线入门),做完这道题就会知道,一个区间的左端点是1,右端点是-1,那么就不用考虑那么多复杂的情况了。
相应的,在这道题里,假设以和x轴平行的作为扫描线,那么对于矩形而言,也一定有入边,出边。
(这图貌似还不错)
然后,线段树作用在什么地方呢?
那么上图:
咳咳,这是样例一的模样(花了好长时间),瞅瞅,扫描线把两个矩形组成的图形成功的分成了三部分开始求(从左向右),分别是黄色,红色,和蓝色区域。。
那么右边那些序号是干嘛的,是表明区间的,1~2,2~3之类的,比如:
扫描线经过x=10的时候, 这个时候,发现1~3是一条入边(用1,-1表示,变量名是flag),那么就求出1~3的区间长度=20-10=10,然后通过线段树递归更新到区间1~4里,那么我们就不用每次找在哪哪个区间有多少长度了。
然后,扫描线继续游荡。。。披荆斩棘,,,好不容易到了x=15,然后发现2~3是一条入边,那么立即计算出长度20-15=5,然后赶紧扔给1~4(递归),但是你们看,,除了第一条边(x=10)没有面积,其他边都可以和前面的边构成矩形,。。所以我们要先算出第一条边。。。。
然后到第二条边的时候就可以黄色区域的面积了,面积等于啥呢?
瞅瞅,是(15-10)*length(1~4),对吧,因为刚才所有的长度都递归到区间1~4(线段树的根)了,这样应该可以懂线段是有啥用了把。。。
建树的时候回溯的条件是tree[rt].l+1==tree[rt].r,这里的l和r就是序号,,,挨着就好了,不需要再分成点了,,,。。
然后就没了。。。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct ppx
{
int l,r;
double left,right;
double len;
int cover;//记录重边情况
} tree[4*200+10];
double y[200+10];//对应的序号装对应的边
struct pp
{
double x;
double y1,y2;
int flag;//标记入边还是出边
} node[200+10];
bool cmp(pp s,pp t)
{
return s.x<t.x;
}
void build(int rt,int left,int right)
{
tree[rt].l=left;
tree[rt].r=right;
tree[rt].left=y[left];
tree[rt].right=y[right];
tree[rt].len=0;
tree[rt].cover=0;
if(tree[rt].l+1==tree[rt].r)
{
return ;
}
int m=(left+right)/2;
build(rt<<1,left,m);
build((rt<<1)+1,m,right);
}
void solve(int rt)
{
if(tree[rt].cover>0)
{
tree[rt].len=tree[rt].right-tree[rt].left;//求这段区间长度
}
else if(tree[rt].r-tree[rt].l==1)//长度清0
{
tree[rt].len=0;
}
else
{
tree[rt].len=tree[rt<<1].len+tree[(rt<<1)+1].len;//长度递归到线段树的根
}
}
void updata(int rt,pp b)//写那么复杂为了节省时间
{
if(b.y1==tree[rt].left&&b.y2==tree[rt].right)//区间查找嘛。。。
{
tree[rt].cover+=b.flag;
}
else if(b.y2<=tree[rt<<1].right)//找左子树。。。
{
updata(rt<<1,b);
}
else if(b.y1>=tree[(rt<<1)+1].left)//找右子树。。。。
{
updata((rt<<1)+1,b);
}
else//分成两段。。。。左子树和右子树,。。。。
{
pp temp;
temp=b;
temp.y2=tree[rt<<1].right;
updata(rt<<1,temp);
temp=b;
temp.y1=tree[(rt<<1)+1].left;
updata((rt<<1)+1,temp);
}
solve(rt);//区间的长度更新啦
}
int main()
{
int n,cases=1;
while(~scanf("%d",&n)&&n)
{
int t=1;
for(int i=1; i<=n; i++)
{
scanf("%lf%lf",&node[t].x,&y[t]);
scanf("%lf%lf",&node[t+1].x,&y[t+1]);
node[t].y1=y[t];
node[t].y2=y[t+1];
node[t+1].y1=y[t];
node[t+1].y2=y[t+1];
node[t].flag=1;
node[t+1].flag=-1;
t+=2;
}
sort(node+1,node+t,cmp);
sort(y+1,y+t);
build(1,1,t-1);//为啥t-1,因为现在t比序号大一了。。。因为上面的循环
updata(1,node[1]);//先算第一条边。。。
double sum=0;
for(int i=2; i<t; i++)
{
sum+=tree[1].len*(node[i].x-node[i-1].x);//瞅见没,这就是求面积的。。
// printf("%lf %lf\n",node[i].x-node[i-1].x,tree[1].len);
updata(1,node[i]);
}
printf("Test case #%d\n",cases++);
printf("Total explored area: %.2f\n\n",sum);
}
}