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;
}
原文地址:https://www.cnblogs.com/scau20110726/p/3018702.html