POJ 1177 Picture(线段树+扫描线)

59 篇文章 0 订阅
5 篇文章 0 订阅

Description
求n个矩形的周长并
Input
第一行为一整数n表示矩形个数,之后n行每行四个整数x1,y1,x2,y2表示该矩形左下端点与右上端点的横纵坐标
Output
输出这n个矩形的周长并
Sample Input
7
-15 0 5 10
-5 8 20 25
15 -4 24 14
0 -6 16 4
2 15 10 22
30 10 36 20
34 0 40 16
Sample Output
228
Solution
类似矩形面积并,首先用左右两边表示一个矩形并标记每条线段是左边还是右边,然后对这些线段按x升序排序,对y轴建树(对每个线段的两端点纵坐标排完序离散化之后建线段树),用一根扫描线从左往右扫这些线段,线段树记录的是当前区间中有多少段不连续的已经被扫描的区间,被完全覆盖的次数,左右端点是否被覆盖,被覆盖的长度,每次扫描线扫到左边就在线段树中覆盖这个区间,遇到右端点就取消这段区间的覆盖,维护这个区间中被连续覆盖部分的数量乘二倍的相邻扫描线间距即为这次扫描部分横边的长度,这个区间被覆盖的长度减去上一根扫描线被覆盖长度的绝对值即为这次扫描部分纵边的长度,具体如何维护见代码
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define maxn 11111
struct Tree
{
    int left,right,num,flag,len;
    bool lf,rf;
}T[4*maxn];
//num表示这个区间被连续覆盖部分的数量 
//flag标记这个区间是否被完全覆盖
//len表示这个区间被覆盖的长度
//lf和rf表示这个区间的左右端点是否被覆盖 
struct Line
{
    int x,y1,y2;
    bool flag;
}line[maxn];
int n,cnt,res,y[maxn],ans,num,len;
void add(int x,int y1,int y2,int yy,int flag)
{
    line[cnt].x=x;line[cnt].y1=y1;line[cnt].y2=y2;line[cnt].flag=flag;y[cnt++]=yy;
}
bool cmp(Line l1,Line l2)
{
    if(l1.x!=l2.x) return l1.x<l2.x;
    return l1.flag>l2.flag;
}
void build(int l,int r,int t)
{
    T[t].left=l;
    T[t].right=r;
    T[t].num=T[t].len=T[t].flag=T[t].lf=T[t].rf=0;
    if(l+1==r)return ;
    int mid=(l+r)>>1;
    build(l,mid,2*t);
    build(mid,r,2*t+1);
} 
void update_num(int t)
{
    if(T[t].flag) T[t].lf=T[t].rf=T[t].num=1;
    else if(T[t].left+1==T[t].right) T[t].lf=T[t].rf=T[t].num=0;
    else
    {
        T[t].lf=T[2*t].lf;
        T[t].rf=T[2*t+1].rf;
        T[t].num=T[2*t].num+T[2*t+1].num-T[2*t].rf*T[2*t+1].lf;
    }
}
void update_len(int t)
{
    if(T[t].flag) T[t].len=y[T[t].right]-y[T[t].left];
    else if(T[t].left+1==T[t].right) T[t].len=0;
    else T[t].len=T[2*t].len+T[2*t+1].len;
}
void update(int l,int r,int z,int t)
{
    if(y[T[t].left]==l&&y[T[t].right]==r) T[t].flag+=z;
    else if(T[t].left+1==T[t].right) return ;
    else
    {
        int mid=(T[t].left+T[t].right)>>1;
        if(r<=y[mid]) update(l,r,z,2*t);
        else if(l>=y[mid]) update(l,r,z,2*t+1);
        else
        {
            update(l,y[mid],z,2*t);
            update(y[mid],r,z,2*t+1);
        }
    }
    update_num(t);
    update_len(t);
}
int main()
{
    while(~scanf("%d",&n))
    {
        cnt=0;ans=0;len=num=0;
        int x1,x2,y1,y2;
        while(n--)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            //用左右两边表示该矩形 
            add(x1,y1,y2,y1,1);
            add(x2,y1,y2,y2,0); 
        }
        sort(line,line+cnt,cmp);//对这些线段按横坐标升序排序
        //对y轴建树 
        sort(y,y+cnt); 
        res=unique(y,y+cnt)-y;
        build(0,res-1,1);
        for(int i=0;i<cnt;i++)
        {
            if(line[i].flag) update(line[i].y1,line[i].y2,1,1);//扫到左边就在线段树中覆盖这个区间 
            else update(line[i].y1,line[i].y2,-1,1);//扫到右边就在线段树中取消这个区间的覆盖 
            if(i) ans+=2*num*(line[i].x-line[i-1].x);//横边长度 
            ans+=abs(T[1].len-len);//纵边长度 
            len=T[1].len;//更新被覆盖的长度 
            num=T[1].num;//更新被连续覆盖部分的数量 
        }
        printf("%d\n",ans);
    } 
    return 0;
} 
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值