研究了线段树计算矩形面积的水题poj1151的实现过程,我又好好研究了用线段树来解决另一道稍微难一点的题:hdu3265,这是09年宁波赛区的一道题,只是在poj1151的基础上更巧妙了一点!
由于题目给出的矩形是回字形,所以我把把一个回字拆开就可以了,也就是说,hdu3265中插入一个poster,相当于插入了四个矩形(也可能只有三个)!
由于题目的数据量比较小,只有5000,而且是整型,所以不用离散化操作!在面前那道题的基础上,去掉离散化就OK了!
把代码贴出来吧!
- #include
- #include
- 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
- }
- //建树
- 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
- load(t,x1,y1,y2,1);t++;
- load(t,x3,y1,y2,-1);t++;
- Max=max(Max,max(y1,y2));
- }
- if(x3
- load(t,x3,y4,y2,1);t++;
- load(t,x4,y4,y2,-1);t++;
- Max=max(Max,max(y4,y2));
- }
- if(x3
- load(t,x3,y1,y3,1);t++;
- load(t,x4,y1,y3,-1);t++;
- Max=max(Max,max(y1,y3));
- }
- if(x4
- 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
- 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轴排序后,矩形的高是很容易求出来的,我们可以不管,但是矩形的宽度就不好求,这时候,线段树的作用就凸显出来了!所以线段树在这里,本质上还是管理线段,而不是矩形!