• 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;
    }
  • 相关阅读:
    RMI几种公布和引用服务的方式
    mysql 多日志表结果集合拼接存储过程
    USRP通信的结构体和常量(上位机、下位机共用)
    Flash Builder4破解步骤
    leetcode 217 Contains Duplicate 数组中是否有反复的数字
    关于权限表的基本设计
    Objective-C之成魔之路【7-类、对象和方法】
    vs2008C1902程序数据库管理不匹配
    配置hadoop集群一
    BZOJ 2338 HNOI2011 数矩形 计算几何
  • 原文地址:https://www.cnblogs.com/scau20110726/p/3018702.html
Copyright © 2020-2023  润新知