前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【HDU 1542】Atlantis(线段树+离散化,矩形面积并)

【HDU 1542】Atlantis(线段树+离散化,矩形面积并)

作者头像
饶文津
发布2020-06-02 15:42:12
3500
发布2020-06-02 15:42:12
举报
文章被收录于专栏:饶文津的专栏

求矩形面积并,离散化加线段树。

扫描线法:

用平行x轴的直线扫,每次ans+=(下一个高度-当前高度)*当前覆盖的宽度。

代码语言:javascript
复制
#include<algorithm>
#include<cstdio>
#include<cstring>
#define dd double
#define ll long long
#define N 201
using namespace std;
struct P{dd s,e,h;int f;}p[N];
struct Tree{dd sum;int c;}t[N<<5];
dd a,b,c,d,x[N],ans;
int n,m,num;
int cmp(const P &a,const P &b){
    return a.h<b.h;
}
void pushUp(ll rt,ll l,ll r){
    if(t[rt].c)t[rt].sum=x[r+1]-x[l];//r+1是因为节点[l,r]表示区间[x[l],x[r+1]]。
    else if(l==r)t[rt].sum=0;
    else t[rt].sum=t[rt<<1].sum+t[rt<<1|1].sum;
}
void update(ll s,ll e,ll rt,ll l,ll r,ll v){
    if(s<=l&&r<=e) t[rt].c+=v;
    else {
        if(l>e||r<s)return;
        ll m=l+r>>1;
        update(s,e,rt<<1,l,m,v);
        update(s,e,rt<<1|1,m+1,r,v);
    }
    pushUp(rt,l,r);
}
int main()
{
    while(scanf("%d",&n),n){
        int k=0;
        for(int i=0;i<n;i++){
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            x[++k]=a,p[k]=(P){a,c,b,1};
            x[++k]=c,p[k]=(P){a,c,d,-1};
        }
        sort(x+1,x+1+k);
        sort(p+1,p+1+k,cmp);
        m=1;
        for(int i=1;i<=k;i++)
            if(x[i]!=x[i-1])x[m++]=x[i];
        ans=0;
        for(int i=1;i<=k;i++){//共k条线段,每次计算p[i].h到p[i+1].h之间的面积,第k次相当于清空所有,酱就不用初始化线段树了。
            int l=lower_bound(x,x+m,p[i].s)-x;
            int r=lower_bound(x,x+m,p[i].e)-x-1;//r-1同上原因
            update(l,r,1,0,m-1,p[i].f);
            ans+=t[1].sum*(p[i+1].h-p[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2lf\n\n",++num,ans);
    }
    return 0;
}

另一种方法还是线段树,这里扫描线用的是平行y轴的直线,每次增加的面积是当前扫描的竖线所在的高度区间的最后一次的x与当前x的差值乘上区间的高度。所以每次增加的不一定是一个矩形,而是多个矩形并。

代码语言:javascript
复制
#include<cstdio>
#include<algorithm>
#define dd double
using namespace std;
#define N 201
struct P{dd x,y1,y2;int f;}p[N];
struct TREE{dd y1,y2,x;int c,f;}tree[N<<5];
dd x1,y1,x2,y2,y[N];
int n,k,num;
int cmp(const P &a,const P &b){
    return a.x<b.x;
}
void build(int i,int l,int r){
    tree[i].c=tree[i].f=0;
    tree[i].y1=y[l];//直接将线段树节点代表的区间存在线段树里
    tree[i].y2=y[r];
    if(l+1==r){
        tree[i].f=1;
        return;
    }
    int mk=(l+r)>>1;
    build(i<<1,l,mk);
    build(i<<1|1,mk,r);
}
dd insert(int i,dd x,dd l,dd r,int flag){
    if(r<=tree[i].y1||l>=tree[i].y2)
        return 0;
    if(tree[i].f){//离散后的一个最小区间,叶子节点
        dd ans;
        if(tree[i].c>0)//全覆盖
            ans=(x-tree[i].x)*(tree[i].y2-tree[i].y1);//(当前x-该区间最后的x)*区间高度
        else
            ans=0;
        tree[i].x=x;//该区间最新的x
        tree[i].c+=flag;//更新覆盖
        return ans;
    }
    return insert(i<<1,x,l,r,flag)+insert(i<<1|1,x,l,r,flag);
}
int main(){
    while(scanf("%d",&n),n){
        k=0;
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            y[++k]=y1,p[k]=(P){x1,y1,y2,1};
            y[++k]=y2,p[k]=(P){x2,y1,y2,-1};
        }
        sort(y+1,y+k+1);
        sort(p+1,p+k+1,cmp);
        //没有去重,其实数量少,没必要。
        build(1,1,k);
        dd ans=0;
        for(int i=1;i<=k;i++)
            ans+=insert(1,p[i].x,p[i].y1,p[i].y2,p[i].f);
        printf("Test case #%d\nTotal explored area: %.2f\n\n",++num,ans);
    }
    return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-07-31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档