hdu(1255)——覆盖的面积(线段树求面积交)

给定平面上若干矩形,求出被这些矩形覆盖过至少两次的区域的面积.

虽说覆盖两次区域的面积,但是这道题实际上就是求矩形的面积交。

膜拜能够想出这种解法的神牛,竟然能把实际的东西用这么抽象的语言表示出来,实在是佩服,现在关于扫描线的题才做了几道,没有对其深刻理解,但是多练总可以理解的,奋斗吧!!ACMer!!我是永远不会服输的。加油!

下面还是附上题解,写的不够详细清楚还请多多见谅。

首先我想说我是看了别人的博客学了思路,然后按照别人的代码来模仿写的。

这里推荐:http://www.cnblogs.com/scau20110726/archive/2013/04/14/3020998.html   http://xuyemin520.is-programmer.com/posts/26000.html(尤其推荐后面那个人写的,代码风格比较适合初学者阅读)

5

1 1 4 2

1 3 3 7

2 1.5 5 4.5

3.5 1.25 7.5 4

6 3 10 7


这是根据第一个样例画的图,我觉得可以跟着代码走一遍,然后就会理解了。

面积交和面积并的区别就在于在线段树中多添加了一个inlen节点,这个的目的是为了保存出现了两次及以上的线段的长度。

然后我们要对线段树的坐标进行离散化,这里我们也是按照x的大小来建树。

主要区别是这里update操作中多了两个函数pushup1,pushup2。它们分别的作用是向上更新只出现过一次的线段的长度以及向上更新出现两次及两次以上的线段的长度。

void pushup1(int v){
    if(tree[v].cover>0) tree[v].len=tree[v].r-tree[v].l;
    else{
        int temp=v<<1;
        tree[v].len=tree[temp].len+tree[temp+1].len;
    }
}
pushup1函数还是和原来一样,当cover>0时,那么len就是整个节点的长度。否则的话,那么就是左右子树中len的和(因为我们不用去管左右子树分别出现过几次,只需要递归求出当前节点的len即可)

void pushup2(int v){
    int temp=v<<1;
    if(tree[v].cover>=2) tree[v].inlen=tree[v].r-tree[v].l;
    else if(tree[v].cover==1){
        tree[v].inlen=tree[temp].len+tree[temp+1].len;
    }
    else tree[v].inlen=tree[temp].inlen+tree[temp+1].inlen;
}
pushup2,函数,当cover>=2时,那么inlen就是当前节点的长度。当cover==1时,这里我想了很久,为什么是左右子树的len加起来就好了呢?后来手动模拟了下,发现如果当前节点cover==1,说明它已经出现过了一次,再加上左右子树的len,就代表的是它加上的肯定是左右子树中出现次数>=1的长度,所以再往上覆盖上去的话,那么就会发现,tree[v].cover就会大于等于2了。

但是另外最后一种情况就是当前v节点cover==0那么它就只能加上左右子树的inlen(也就是出现次数大于等于2的次数的长度

最后我们回溯上去,使父节点tree[1].inlen就是当前区间的高。

代码:

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
#define maxn 2222
double ym[maxn];
struct node{
    double l,r;
    double len,inlen;
    int cover;
}tree[maxn*4];
struct nn{
    double x,y1,y2;
    int flag;
}t[maxn];
bool cmp(nn a,nn b){
    return a.x<b.x;
}
void pushup1(int v){
    if(tree[v].cover>0) tree[v].len=tree[v].r-tree[v].l;
    else{
        int temp=v<<1;
        tree[v].len=tree[temp].len+tree[temp+1].len;
    }
}
void pushup2(int v){
    int temp=v<<1;
    if(tree[v].cover>=2) tree[v].inlen=tree[v].r-tree[v].l;
    else if(tree[v].cover==1){
        tree[v].inlen=tree[temp].len+tree[temp+1].len;
    }
    else tree[v].inlen=tree[temp].inlen+tree[temp+1].inlen;
}
void build(int l,int r,int v){
    tree[v].l=ym[l];
    tree[v].r=ym[r];
    tree[v].len=tree[v].inlen=tree[v].cover=0;
    if(l+1==r)  return ;
    int mid=(l+r)>>1;
    int temp=v<<1;
    build(l,mid,temp);
    build(mid,r,temp+1);
}
void update(int v,struct nn b){
    if(tree[v].l==b.y1&&tree[v].r==b.y2){
        tree[v].cover+=b.flag;
        pushup1(v);
        pushup2(v);
        return ;
    }
    int temp=v<<1;
    if(b.y1>=tree[temp].r) update(temp+1,b);
    else if(b.y2<=tree[temp+1].l) update(temp,b);
    else{
        struct nn y;
        y=b;
        y.y2=tree[temp].r;
        update(temp,y);
        y=b;
        y.y1=tree[temp+1].l;
        update(temp+1,y);
    }
    pushup1(v);
    pushup2(v);
}
int main(){
    int T;
    while(~scanf("%d",&T)){
        int n;
        while(T--){
            memset(ym,0,sizeof(ym));
            memset(tree,0,sizeof(tree));
            memset(t,0,sizeof(t));
            scanf("%d",&n);
            double x1,y1,x2,y2;
            int j=1;
            for(int i=1;i<=n;i++){
                scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
                t[j].flag=1;
                t[j].x=x1;
                t[j].y1=y1;
                t[j].y2=y2;
                ym[j]=y1;
                j++;
                t[j].flag=-1;
                t[j].x=x2;
                t[j].y1=y1;
                t[j].y2=y2;
                ym[j]=y2;
                j++;
            }
            sort(ym+1,ym+j);
            sort(t+1,t+j,cmp);
            build(1,j-1,1);
            update(1,t[1]);
            double sum=0;
            for(int i=2;i<j;i++){<span style="white-space:pre">			</span>//注意这里循环直到j为止
                sum+=(t[i].x-t[i-1].x)*tree[1].inlen;
                update(1,t[i]);
            }
            printf("%.2lf\n",sum);
        }
    }
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值