hdu 1828 Picture (扫描线+线段树 求多矩形总周长)

题目链接:哆啦A梦传送门

题意:给出n个矩形围城的多边形,求它们的总周长。

题解:

参考博客:

hdu 1542解法差不多,也是扫描线求。

对于扫描线从下往上扫,每次横轴的长度为=|上一次的有效长度-这次的有效长度|

对于竖轴的长度,我们在每个节点添加lc,rc,num,分别表示该节点的左右端点是否被覆盖(lc,rc为1表示被覆盖),num表示此节点代表的区间里有多少条线段。

那么对于完全覆盖的节点,它们的lc,rc,num都为1。

对于单节点,它们的lc,rc,num都为0。

对于不完全覆盖的节点,他的lc等于该节点的左子节点的lc,rc等于该节点的右子节点的rc,num等于左右子节点的num相加再减去两区间合并时是否对覆盖产生的影响(左子节点的rc&右子节点的lc(为1说明有重叠))。

 

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long LL;
const int N=10000;

struct node{
    double l,r,h; ///每条线的左右x端点,该线的纵坐标
    int d; ///d=1为下边,d=-1为上边
}a[N];

bool cmp(node X,node Y)
{
    return X.h<Y.h;
}

///存储横坐标,离散化使用
double x[N];

struct Node{
    int tl,tr;
    int s;///该节点被是否被完全覆盖,s不为0即为完全覆盖
    int len,num; ///该节点的有效长度,该节点里有多少条线段
    int lc,rc; ///该节点的左右端点是否被覆盖
}tree[N<<2];

void build(int root,int l,int r)
{
    tree[root].tl=l;
    tree[root].tr=r;
    tree[root].len=tree[root].s=0;
    tree[root].num=tree[root].lc=tree[root].rc=0;

    if(l==r) return ;
    else{
        int mid=(l+r)>>1;
        build(root*2,l,mid);
        build(root*2+1,mid+1,r);
    }
}

void pushup(int root)
{
    if(tree[root].s){ ///该节点被完全覆盖
        tree[root].len=x[tree[root].tr]-x[tree[root].tl-1];
        tree[root].lc=tree[root].rc=1;
        tree[root].num=1;
    }
    else if(tree[root].tl==tree[root].tr){ ///这是一个点而不是一条线段
        tree[root].len=0;
        tree[root].lc=tree[root].rc=tree[root].num=0;
    }
    else {///是一条没有被完全覆盖的区间,合并左右子节点
        tree[root].len=tree[root*2].len+tree[root*2+1].len;
        tree[root].lc=tree[root*2].lc;
        tree[root].rc=tree[root*2+1].rc;
        tree[root].num=tree[root*2].num+tree[root*2+1].num-(tree[root*2].rc&tree[root*2+1].lc);
    }
}

void update(int root,int ql,int qr,int val)
{
    int tl=tree[root].tl,tr=tree[root].tr;

    if(ql<=tl&&tr<=qr){ ///更新区间包含该节点区间,说明该节点区间被完全覆盖
        tree[root].s+=val;
        pushup(root);
        return;
    }
    int mid=(tl+tr)>>1;
    if(mid>=ql) update(root*2,ql,qr,val);
    if(mid<qr) update(root*2+1,ql,qr,val);
    pushup(root);

}
int main()
{

    int n,T=0;

    while(~scanf("%d",&n))
    {
        int m=0;
        for(int i=1;i<=n;i++)
        {
            double x1,x2,y1,y2;
            scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
            a[m].l=x1,a[m].r=x2,a[m].h=y1,a[m].d=1;
            x[m++]=x1;

            a[m].l=x1,a[m].r=x2,a[m].h=y2,a[m].d=-1;
            x[m++]=x2;

        }

        sort(x,x+m);
        sort(a,a+m,cmp);

        int len=unique(x,x+m)-x;

//        printf("len=%d\n",len);
//        for(int i=0;i<=len;i++)
//            printf("%d=%f ",i,x[i]);
//        puts("");


        build(1,1,len);

        double sum=0;
        double last=0;

        for(int i=0;i<m;i++) ///离散化各点,并更新线段树
        {
            ///因为我们的树节点是要记录线段的长度,而不是点的权值
            ///这里我们可以将a[i].l的位置+1,这样在更新的时候我们就可以a[r]-a[l-1]来计算长度
            ///这里其实每个叶子节点都代表相邻x的一小段长度
            int l=lower_bound(x,x+len,a[i].l)-x+1;
            int r=lower_bound(x,x+len,a[i].r)-x;

            update(1,l,r,a[i].d);
            ///tree[1].len表示整个区间的有效长度
//            sum+=(a[i+1].h-a[i].h)*tree[1].len;
            sum+=fabs(tree[1].len-last);///求上下边长
            sum+=(a[i+1].h-a[i].h)*tree[1].num*2; ///求左右边长
            last=tree[1].len;
        }
        printf("%.0f\n",sum);

    }
    return 0;
}

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值