(中等) HDU 1828 Picture,扫描线。

本文介绍了如何通过编程计算多个矩形并集的周长,并详细解释了使用线段树和扫描线算法进行优化的方法。包括了具体的代码实现和实例分析。
  Problem Description
  A number of rectangular posters, photographs and other pictures of the same shape are pasted on a wall. Their sides are all vertical or horizontal. Each rectangle can be partially or totally covered by the others. The length of the boundary of the union of all rectangles is called the perimeter.

  Write a program to calculate the perimeter. An example with 7 rectangles is shown in Figure 1.



The corresponding boundary is the whole set of line segments drawn in Figure 2.



  The vertices of all rectangles have integer coordinates.
 
  题目就是求矩形并的周长和。
  这个题其实和求矩形的面积和没有什么区别,线段树维护的是覆盖区间的总长度,然后扫描线是从左到右扫描,每次计算一个竖边的时候,这一次区间覆盖长度减去上一次区间覆盖长度的绝对值就是变化量,要加到ans上。
  然后就是计算横边的时候有点麻烦,可以再从下到上扫描一遍,从新弄一个线段树。
  不过根据那个杭电大神的做法,是同时再维护三个线段树,分别表示区间内线段端点的个数,区间内左边界是否被覆盖,右边界是否被覆盖,后两个线段树是用来辅助计算前面那个的。然后x的差值乘上线段的个数就是横边的了。
  不过这里要注意先计算还是先更新的问题,横边要先计算,然后更新,然后竖边计算。
 
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

#define lc po*2
#define rc po*2+1
#define lson L,M,lc
#define rson M+1,R,rc

using namespace std;

const int maxn=20004;

struct BIAN
{
    int x,y1,y2;
    short state;
};

BIAN bian[10004];
bool vis[maxn];
int BIT[maxn*4];
int COL[maxn*4];

bool lBIT[maxn*4],rBIT[maxn*4];
int coubian[maxn*4];

void pushUP(int L,int R,int po)
{
    if(COL[po])
    {
        BIT[po]=R-L+1;
        lBIT[po]=rBIT[po]=1;
        coubian[po]=2;
    }
    else if(L==R)
    {
        BIT[po]=0;
        lBIT[po]=rBIT[po]=0;
        coubian[po]=0;
    }
    else
    {
        BIT[po]=BIT[lc]+BIT[rc];
        lBIT[po]=lBIT[lc];
        rBIT[po]=rBIT[rc];
        coubian[po]=coubian[lc]+coubian[rc];

        if(lBIT[rc]&&rBIT[lc])
            coubian[po]-=2;
    }
}

void update(int ul,int ur,int ut,int L,int R,int po)
{
    if(ul<=L&&ur>=R)
    {
        COL[po]+=ut;
        pushUP(L,R,po);

        return;
    }

    int M=(L+R)/2;

    if(ul<=M)
        update(ul,ur,ut,lson);
    if(ur>M)
        update(ul,ur,ut,rson);

    pushUP(L,R,po);
}

bool cmp(BIAN a,BIAN b)
{
    return a.x<b.x;
}

int main()
{
    int N;
    int x1,x2,y1,y2;
    int ans,last;

    while(~scanf("%d",&N))
    {
        ans=0;
        last=0;
        memset(COL,0,sizeof(COL));
        memset(BIT,0,sizeof(BIT));
        memset(vis,0,sizeof(vis));
        memset(lBIT,0,sizeof(lBIT));
        memset(rBIT,0,sizeof(rBIT));
        memset(coubian,0,sizeof(coubian));

        for(int i=1;i<=N;++i)
        {
            scanf("%d %d %d %d",&x1,&y1,&x2,&y2);

            bian[i*2-1].x=x1;
            bian[i*2-1].y1=y1+10001;
            bian[i*2-1].y2=y2+10001;
            bian[i*2-1].state=1;

            bian[i*2].x=x2;
            bian[i*2].y1=y1+10001;
            bian[i*2].y2=y2+10001;
            bian[i*2].state=-1;
        }

        sort(bian+1,bian+2*N+1,cmp);

        for(int i=1;i<=2*N;++i)
        {
            ans+=coubian[1]*(bian[i].x-bian[i-1].x);

            update(bian[i].y1,bian[i].y2-1,bian[i].state,1,20001,1);

            ans+=abs(BIT[1]-last);
            last=BIT[1];
        }

        printf("%d\n",ans);
    }

    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/whywhy/p/4214213.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值