poj1177 Picture 线段树+扫描线+离散化

题目链接:http://poj.org/problem?id=1177

题意:
题目就是说n个矩形组成的新图形的周长,每个矩形会给出左下角和右上角的坐标。

分析:
线段树+扫描线+离散化,我是从左到右扫描的,需要将纵坐标离散化,然后每扫描到一根线,求出其覆盖y方向的总长度,减去上次求得的长度,即为本条线增加或减少的长度,同时可以求出每两条线之间的距离,即为横坐标方向的周长的一部分,由于是矩形构成的,横坐标方向是为周长的部分一定成对出现,同时需要用到一个很重要的记录(num),因为可能出现两条竖线之间有4,6…甚至更多条外围的线段。

源代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
using namespace std;
const int N=5005;
typedef __int64 ll;

struct seg
{
    int y1,y2;
    int x;
    int flag;
    seg(){}
    seg(int _y1,int _y2,int _x,int f):y1(_y1),y2(_y2),x(_x),flag(f){}
    friend bool operator < (seg a,seg b)
    {
        if(a.x==b.x)
            return a.flag>b.flag;
        return a.x<b.x;
    }
}Line[N<<1];

struct TNode
{
    int l,r;
    int num;//子树中有多少条线段
    int cover;//是否被覆盖,入边加1,出边-1
    int len;//当前节点覆盖y方向的总长度
    bool lb,rb;//左右节点是否被覆盖
}tre[N<<3];
vector<int>vec;

void build(int o,int l,int r)
{
    tre[o].l=l;
    tre[o].r=r;
    tre[o].num=0;
    if(l==r-1)
        return;
    int mid=(l+r)/2;
    build(o<<1,l,mid);
    build(o<<1|1,mid,r);
}

void update_len(int o)
{
    if(tre[o].cover>0)
    {
        tre[o].len=vec[tre[o].r]-vec[tre[o].l];
        tre[o].lb=tre[o].rb=true;
        tre[o].num=1;
        return;
    }
    if(tre[o].l==tre[o].r-1)
    {
        tre[o].cover=tre[o].lb=tre[o].rb=0;
        tre[o].num=tre[o].len=0;
        return;
    }
    tre[o].lb=tre[o<<1].lb;
    tre[o].rb=tre[o<<1|1].rb;
    tre[o].len=tre[o<<1].len+tre[o<<1|1].len;
    tre[o].num=tre[o<<1].num+tre[o<<1|1].num-(tre[o<<1].rb&tre[o<<1|1].lb);
}

void update(int o,seg p)
{
    if(p.y1<=vec[tre[o].l]&&p.y2>=vec[tre[o].r])
    {
        tre[o].cover+=p.flag;
        update_len(o);
        return;
    }
    if(tre[o].l==tre[o].r-1)
        return;
    int mid=(tre[o].l+tre[o].r)/2;
    if(p.y1<=vec[mid])
        update(o<<1,p);
    if(p.y2>vec[mid]) update(o<<1|1,p);
    update_len(o);
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int k=0;
        for(int i=0;i<n;i++)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            Line[k++]=seg(y1,y2,x1,1);//进
            //y[k]=y1;
            Line[k++]=seg(y1,y2,x2,-1);//出
            //y[k]=y2;
            vec.push_back(y1);
            vec.push_back(y2);
        }

        sort(vec.begin(),vec.end());
        vec.erase(unique(vec.begin(),vec.end()),vec.end());
        sort(Line,Line+k);
        build(1,0,vec.size()-1);
        //printf("1111\n");
        int ans=0;
        int old=0;
        int lines=0;
        for(int i=0;i<k;i++)
        {
            //printf("%d\n",Line[i].x);
            update(1,Line[i]);
            if(i>=1)
            {
                ans+=lines*2*(Line[i].x-Line[i-1].x);
            }
            ans+=abs(tre[1].len-old);
            old=tre[1].len;
            lines=tre[1].num;
        }
        printf("%d\n",ans);
    }
    return 0;
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值