您的位置:首页 > 其它

线段树的学习之:如何用线段树计算矩形面积(二)

2012-11-03 10:09 232 查看
研究了线段树计算矩形面积的水题poj1151的实现过程,我又好好研究了用线段树来解决另一道稍微难一点的题:hdu3265,这是09年宁波赛区的一道题,只是在poj1151的基础上更巧妙了一点!

由于题目给出的矩形是回字形,所以我把把一个回字拆开就可以了,也就是说,hdu3265中插入一个poster,相当于插入了四个矩形(也可能只有三个)!

由于题目的数据量比较小,只有5000,而且是整型,所以不用离散化操作!在面前那道题的基础上,去掉离散化就OK了!

把代码贴出来吧!

#include<stdio.h>
#include<algorithm>
using namespace std;
#define M 50001
typedef long long llong;
struct Tree{
int ll,rr,cover;//这里的cover之所以用llong而不用bool是因为可能出现多条边重叠
int len;
}tree[4*M];
struct Line{
int x,y1,y2;
int flag;
}line[8*M];
//比较函数
bool cmp(const Line &l1,const Line &l2){//排序的比较函数
if(l1.x==l2.x){
return l1.flag>l2.flag;
}
return l1.x<l2.x;
}
//建树
void build(int id,int ll,int rr){
tree[id].ll=ll;tree[id].rr=rr;
tree[id].len=tree[id].cover=0;
if(ll+1==rr)return;//如果已经是元线段,则不用拆分了
int mid=(ll+rr)>>1;
build(id*2,ll,mid);
build(id*2+1,mid,rr);
}
void lenght(int id){//求用于计算的线段实际长度
if(tree[id].cover>0){
tree[id].len=tree[id].rr-tree[id].ll;
}else if(tree[id].ll+1==tree[id].rr){//元线段
tree[id].len=0;
}else
tree[id].len=tree[id*2].len+tree[id*2+1].len;
}
//更新树
void update(int id,Line line){
if(tree[id].ll==line.y1&&tree[id].rr==line.y2){
tree[id].cover+=line.flag;
lenght(id);
return;
//这里的比较有点特别,值得关注一下,
}else if(line.y1>=tree[2*id+1].ll){//用右孩子的左边界来比较
update(id*2+1,line);
}else if(line.y2<=tree[id*2].rr){// 左孩子的右边界来比较
update(id*2,line);
}else{//分跨两个边界
Line tmp;
tmp=line;tmp.y2=tree[id*2].rr;
update(2*id,tmp);
tmp=line;tmp.y1=tree[id*2+1].ll;
update(2*id+1,tmp);
}
lenght(id);//回溯的时候 修改,使根结点的len实时更新
}
void load(int id,int x,int y1,int y2,int flag){
line[id].x=x;
line[id].y1=y1;
line[id].y2=y2;
line[id].flag=flag;
}

int main(){
int n;
int i,t;
int Max=1;
while(scanf("%d",&n)&&n){
int x1,y1,x2,y2,x3,y3,x4,y4;
for(t=i=1;i<=n;i++){
scanf("%d %d %d %d %d %d %d %d",&x1,&y1,&x2,&y2,&x3,&y3,&x4,&y4);
++x1,++y1,++x2,++y2,++x3,++y3,++x4,++y4;//由于起点是0,而线段树管理的起点是1,所以整体平移一个单位
if(x1<x3&&y1<y2){
load(t,x1,y1,y2,1);t++;
load(t,x3,y1,y2,-1);t++;
Max=max(Max,max(y1,y2));
}
if(x3<x4&&y4<y2){
load(t,x3,y4,y2,1);t++;
load(t,x4,y4,y2,-1);t++;
Max=max(Max,max(y4,y2));
}
if(x3<x4&&y1<y3){
load(t,x3,y1,y3,1);t++;
load(t,x4,y1,y3,-1);t++;
Max=max(Max,max(y1,y3));
}
if(x4<x2&&y1<y2){
load(t,x4,y1,y2,1);t++;
load(t,x2,y1,y2,-1);t++;
Max=max(Max,max(y1,y2));
}
}
sort(line+1,line+t,cmp);
build(1,1,Max);//用y的最大值来建树
update ( 1, line[1]);//第一条边一定是入边
llong ans=0;
for(i=2;i<t;i++){
ans+=(llong)tree[1].len*(llong)(line[i].x-line[i-1].x);
update(1,line[i]);//最后一条边肯定是出边,不用考虑
}
printf("%I64d\n",ans);

}
return 0;
}

经过这几天的思考,我对线段树的认识也更深入了一些!我们用线段树解决矩形相关的问题时,容易走进一个误区,那就是矩形是二维的,而通常我们学习的线段树是一维的!用一维的线段树来操作二维的区间,是很难让人想通其中的细节!

但实际上,线段树操作的依然是一维的!这就需要我们把线段树操作线段(比如线段着色)的细节弄清楚!从我博客中关于线段树的两道题可以看出,线段树管理的只是X轴或者y轴的线段,

怎么管理呢?线段的插入或者删除!所以我理解到的用线段树线段树求矩形面积的本质就是:用线段树来插入或者删除线段,再本质一点:线段树的更新区间操作!

再回到矩形的面积,用线段树其实用到了一种分割的思想,把原来的三个矩形分割成了5个矩形,X轴排序后,矩形的高是很容易求出来的,我们可以不管,但是矩形的宽度就不好求,这时候,线段树的作用就凸显出来了!所以线段树在这里,本质上还是管理线段,而不是矩形!





内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息