hdu 1828 Picture

两种方法

详细分析:线段树辅助——扫描线法计算矩形周长并(轮廓线)

第一种,对横线和竖线做相同的操作

/*

对横线和竖线做两次一样的操作

这题不需要离散化

*/

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

#define N 5010

#define MAX 10010

#define lch(i) ((i)<<1)

#define rch(i) ((i)<<1|1)



int res;

struct segment

{

    int l,r,h,v;

}sx[2*N],sy[2*N];

struct node{

    int l,r,cnt,len;

    int mid()

    { return (l+r)>>1; }

}t[2*MAX*4];



int cmp(struct segment p ,struct segment q)

{

    return p.h<q.h;

}



void build(int l ,int r ,int rt)

{

    t[rt].l=l; t[rt].r=r; t[rt].cnt=t[rt].len=0;

    if(l==r) return ;

    int mid=t[rt].mid();

    build(l,mid,lch(rt));

    build(mid+1,r,rch(rt));

}



void cal(int rt)

{

    if(t[rt].cnt) //可以直接计算

        t[rt].len=t[rt].r-t[rt].l+1;

    else if(t[rt].l == t[rt].r) //叶子节点

        t[rt].len=0;

    else //由孩子信息所得

        t[rt].len=t[lch(rt)].len + t[rch(rt)].len;

}



void updata(int l ,int r ,int v ,int rt)

{

    if(t[rt].l==l && t[rt].r==r) //目标区间

    {

        t[rt].cnt += v;

        cal(rt);

        return ;

    }

    int mid=t[rt].mid();

    if(r<=mid)      updata(l,r,v,lch(rt));

    else if(l>mid)  updata(l,r,v,rch(rt));

    else

    {

        updata(l,mid,v,lch(rt));

        updata(mid+1,r,v,rch(rt));

    }

    cal(rt);

}



void solve(int left ,int right ,struct segment *s , int n)

{

    build(left,right-1,1); //注意线段树建树,用点建树和用段建树的区别

    int ans=0 , prelen=0;

    for(int i=0; i<n; i++) //updata所有的线段

    {

        updata(s[i].l , s[i].r-1 , s[i].v ,1);

        ans += abs(t[1].len-prelen);

        prelen=t[1].len;

    }

    res += ans;

}



int main()

{

    int n;

    while(scanf("%d",&n)!=EOF)

    {

        if(n==0)

        { puts("0"); continue;}

        int i,j, maxx=-MAX, minx=MAX, maxy=-MAX, miny=MAX;

        for(i=0,j=0; i<n; i++,j+=2)

        {

            int x1,y1,x2,y2;

            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

            maxx=max(maxx,x2);  minx=min(minx,x1);

            maxy=max(maxy,y2);  miny=min(miny,y1);

            //保存横线的信息

            sx[j].l=x1;   sx[j].r=x2;   sx[j].h=y1;   sx[j].v=1;

            sx[j+1].l=x1; sx[j+1].r=x2; sx[j+1].h=y2; sx[j+1].v=-1; 

            //保存竖线的信息

            sy[j].l=y1;   sy[j].r=y2;   sy[j].h=x1;   sy[j].v=1;

            sy[j+1].l=y1; sy[j+1].r=y2; sy[j+1].h=x2; sy[j+1].v=-1;

        }

        sort(sx,sx+j,cmp); //对横线按竖直高度排序

        sort(sy,sy+j,cmp); //对竖线按水平高度排序



        res=0;

        solve(minx,maxx,sx,j); //对横线处理一次

        solve(miny,maxy,sy,j); //对竖线处理一次

        printf("%d\n",res);

    }

    return 0;

}

第二种:只扫描一次,每次移动都计算两部分值,横线值和竖线值,关键要理解lp,rp,num的作用

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

#define N 5010

#define MAX 10100

#define lch(i) ((i)<<1)

#define rch(i) ((i)<<1|1)



struct segment{

    int l,r,h,v;

}s[2*N];

struct node{

    int l,r,lp,rp,cnt,len,num;

    int mid()

    { return (l+r)>>1; }

}t[2*MAX*4];



int cmp(struct segment p ,struct segment q)

{

    return p.h<q.h;

}



void build(int l ,int r, int rt)

{

    t[rt].l=l; t[rt].r=r;

    t[rt].cnt=t[rt].len=0;

    t[rt].lp=t[rt].rp=t[rt].num=0;

    if(l==r) return ;

    int mid=t[rt].mid();

    build(l,mid,lch(rt));

    build(mid+1,r,rch(rt));

}



void cal(int rt)

{

    if(t[rt].cnt)

    {

        t[rt].len=t[rt].r-t[rt].l+1;

        t[rt].lp=t[rt].rp=1;

        t[rt].num=1;

    }

    else if(t[rt].l == t[rt].r) //叶子

    {

        t[rt].len=0;

        t[rt].lp=t[rt].rp=0;

        t[rt].num=0;

    }

    else

    {

        t[rt].len=t[lch(rt)].len+t[rch(rt)].len;

        t[rt].lp=t[lch(rt)].lp; 

        t[rt].rp=t[rch(rt)].rp;

        t[rt].num=t[lch(rt)].num + t[rch(rt)].num - (t[lch(rt)].rp&t[rch(rt)].lp);

    }

}



void updata(int l ,int r ,int v ,int rt)

{

    if(t[rt].l==l && t[rt].r==r) //目标区间

    {

        t[rt].cnt += v;

        cal(rt);

        return ;

    }

    int mid=t[rt].mid();

    if(r<=mid)     updata(l,r,v,lch(rt));

    else if(l>mid) updata(l,r,v,rch(rt));

    else

    {

        updata(l,mid,v,lch(rt));

        updata(mid+1,r,v,rch(rt));

    }

    cal(rt);

}



int main()

{

    int n;

    while(scanf("%d",&n)!=EOF)

    {

        if(n==0) 

        { puts("0"); continue; }

        int i,maxx,minx,m;

        for(i=0,m=0,maxx=-MAX,minx=MAX; i<n; i++,m+=2)

        {

            int x1,y1,x2,y2;

            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);

            maxx=max(maxx,x2);

            minx=min(minx,x1);

            s[m].l=x1;   s[m].r=x2;   s[m].h=y1;   s[m].v=1;

            s[m+1].l=x1; s[m+1].r=x2; s[m+1].h=y2; s[m+1].v=-1;

        }

        sort(s,s+m,cmp);

        build(minx,maxx-1,1);

        int res=0 , prelen=0;

        s[m]=s[m+1]; //每次处理循环的最后一次

        for(int i=0; i<m; i++)

        {

            updata(s[i].l,s[i].r-1,s[i].v,1);

            res += abs(t[1].len-prelen); //计算横线部分

            res += (s[i+1].h-s[i].h)*(2*t[1].num); //计算竖线部分

            prelen=t[1].len;

        }

        printf("%d\n",res);

    }

    return 0;

}

 

你可能感兴趣的:(HDU)