HDU 1828 Picture(线段树:扫描线)

HDU 1828 Picture(线段树:扫描线)

http://acm.hdu.edu.cn/showproblem.php?pid=1828

题意:

        给你n个边平行与坐标轴的矩形,矩形可能重叠,问你所有矩形构成的图形的轮廓线多长(即外周长+内部中空的周长)?

分析:

       本题和HDU1542类似,详解请见: http://blog.csdn.net/u013480600/article/details/22548393

下面继续拿HDU1542的两个矩形为例分析:

HDU 1828 Picture(线段树:扫描线)_第1张图片

不过由于本题求得是周长,所以将所有上下位边按y从小到大排序之后:

首先我们读入第一条扫描线(下位边+1),这条扫描线的长度直接加入ans结果中.

现在线段树整体就有两条竖向边了(平行于y轴的),所以这两条竖向边从第一条扫描线到第二条扫描线之间是会增涨的,所以总周长ans+=2*(第二条扫描线的高h2-第一条扫描线的高h1).

然后我们读入第二条扫描线(下位边+1):

由于我们更新完这条扫描线后,sum[1]覆盖的范围扩大了5,所以ans先加5.其次这个时候,我们的竖向边由两条变成了3条,所以ans+=3*(扫描线3的高度h3-扫描线2的高度h2).

然后我们读入第三条扫描线(上位边-1):

由于我们读入该扫描线后sum[1]的覆盖值少了5,但是此时我们看图知道其实这个矩形的另一条上边了.所以我们不能用ans+=sum[1]了,我要执行ans+=abs(last-sum[1]),其中last是上一轮读入扫描线后sum[1]的值.(这里要注意一下,结合我们读入前两条扫描线的情况分析下,其实每次ans首先加的值都应该是abs(last-sum[1]),然后才是竖向边增加的部分.)

后面基本类似,可以分析出来.

根据上面的分析现在来分析线段树应该维护的信息.其中这里的线段树与HDU1542的线段树一样,其叶节点维护的是一个一个的区间,而不是x轴坐标点,要记住,线段树需要维护的信息有:

1.    cnt(根本信息,只能通过update修改): 表示当前节点控制的区间中 下位边数目-上位边数目的结果

如果cnt==-1,那么表示其儿子节点的cnt不一致

2.    sum(辅助查询信息,可以通过cnt的值改变而被改变): 表示节点控制的区间中被覆盖的边的总长度

3.    pre: 表示节点控制的区间中被被覆盖的边的前缀长度

4.    suf: 表示节点控制的区间中被被覆盖的边的后缀长度

5.    numseg:表示节点控制的区间中竖向边的个数

 

下面分析线段树的基本操作:

1.    PushDown(i,l,r):如果cnt!=-1的话,那么就修改子树的各类信息.

2.    PushUp(i,l,r):根据儿子节点的各类信息修改父节点的各类信息,注意如果左儿子的后缀和右儿子的前缀都不为0,那么父节点的numsge需要-2.

3.    build函数需要初始化cnt,numseg,pre,suf,sum 都为0,所以可以直接用memset初始化即可.不用build递归了.

4.    update类似于:HDU1542的update

 

下面用另一种方式来写代码:要做点小修改,其中pre和suf不再是表示前缀和后缀长度了,而是用来表示是否存在前缀和后缀.另外各个树节点的cnt值不上传也不下传,仅仅保存在自己这个节点就可以.所以不需要PushDown函数了且cnt的值只能>=0了,不存在-1的结果.

但是还需要PushUp函数把sum,pre,suf,numseg等信息传递给父节点.所以PushUp实现:

如果当前节点的cnt值不为0,那么当前节点控制的区域肯定被完全覆盖了,sum=r-l+1,numseg=2,pre=suf=1.

否则cnt=0的话,此时如果l==r,即该节点是叶节点,那么sum=numseg=pre=suf=0

如果此时l不等于r,即该节点不是叶节点, 且cnt此时是0,所以本节点的信息要从子节点获取了.

 

update(ql,qr,i,l,r):如果[ql,qr]区间包括了[l,r]区间,那么直接更新当前节点信息,否则递归处理左右儿子,然后在PushUp即可.记住这里不用PushDown,因为cnt(根本信息)不传递.

这种方式需要仔细体会,cnt的值固定在某个节点,不上传也不下传.

注意:由于区间[l,r]被分成了区间[l,m],和区间[m+1,r],我们要保证这两个区间内都应该有值,否则如果m=r的话,就可能出现无限递归.比如:

void update(int ql,int qr,int v,int i,int l,int r)

{

   if(ql<=l&&r<=qr)

    {

        cnt[i]+=v;

        PushUp(i,l,r);

        return ;

    }

int m=(l+r)/2;

if(ql<=m) update(ql,qr,v,lson);

    if(m<qr)update(ql,qr,v,rson);

    PushUp(i,l,r);

}

上面这个函数如果update(-1,-1,0,9,-1,0)的时候,此时的l=-1,r=0,m=0,

ql=-1,qr=-1,直接就产生了无限递归,看看是不是这样.我们如何保证不产生无限递归呢?只需要保证两个子区间都不空就可以,那么每次update的区间都会减少.由于我们是划分[l,m][m+1,r]的区间,所以这个m一定要比r,m不能等于r,所以m应该是靠近l划分的.

下面有3种划分方式:

m=(l+r)/2 :该方式使得m0的方向走,如果r=0的话,m就靠近r

m=l+(r-l)/2:该方式是使得m靠近l划分

m=(l+r)>>1:该方式也是使得m靠近l划分.

综上所述,m的求法一定要小心.如果lr非负的时候上面3种方式都可以,但是如果有负数,就要用后面两种划分方式.

AC代码:0ms

 

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=22222;
#define lson i*2,l,m
#define rson i*2+1,m+1,r
int cnt[MAXN*4],suf[MAXN*4],pre[MAXN*4],numseg[MAXN*4],sum[MAXN*4];
struct node
{
    int l,r,h,d;
    node(){}
    node(int a,int b,int c,int d):l(a),r(b),h(c),d(d){}
    bool operator < (const node & b)const
    {
        if (h == b.h) return d > b.d;//这句话不写也AC,但是还是写上保险,对于本题来说写不写没区别
        return h<b.h;
    }
}nodes[MAXN];
void PushUp(int i,int l,int r)
{
    if(cnt[i])
    {
        numseg[i]=2;
        pre[i]=suf[i]=1;
        sum[i]=r-l+1;
    }
    else if(l==r)
        numseg[i]=pre[i]=suf[i]=sum[i]=0;
    else
    {
        numseg[i]=numseg[i*2]+numseg[i*2+1];
        if(suf[i*2] && pre[i*2+1]) numseg[i]-=2;
        sum[i]=sum[i*2]+sum[i*2+1];
        pre[i]=pre[i*2];
        suf[i]=suf[i*2+1];
    }
}
void update(int ql,int qr,int v,int i,int l,int r)
{
    if(ql<=l&&r<=qr)
    {
        cnt[i]+=v;
        PushUp(i,l,r);
        return ;
    }
    int m=l+(r-l)/2;//这里一定小心,如果是m=(l+r)/2,会无限递归,栈溢出,如ql=qr=-1且l=-1,r=0的时候
    if(ql<=m) update(ql,qr,v,lson);
    if(m<qr) update(ql,qr,v,rson);
    PushUp(i,l,r);
}
int main()
{
    int t;
    while(scanf("%d",&t)==1)
    {
        int m=0;
        int lbd=10000,rbd=-10000;
        for(int i=1;i<=t;i++)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            lbd=min(lbd,x1);
            rbd=max(rbd,x2);
            nodes[++m]= node(x1,x2,y1,1);
            nodes[++m]= node(x1,x2,y2,-1);
        }
        sort(nodes+1,nodes+m+1);
        int ans=0,last=0;;
        for(int i=1;i<=m;i++)
        {
            int ql=nodes[i].l;
            int qr=nodes[i].r-1;
            if(ql<=qr)update(ql,qr,nodes[i].d,1,lbd,rbd-1);
            ans += abs(last-sum[1]);
            last=sum[1];
            if(i<m) ans+= numseg[1]*(nodes[i+1].h-nodes[i].h);
        }
        printf("%d\n",ans);
    }
}


你可能感兴趣的:(ACM)