[USACO5.5]矩形周长Picture

题目传送门:https://www.luogu.org/problemnew/show/P1856


[USACO5.5]矩形周长Picture

题目背景

墙上贴着许多形状相同的海报、照片。它们的边都是水平和垂直的。每个矩形图片可能部分或全部的覆盖了其他图片。所有矩形合并后的边长称为周长。

题目描述

编写一个程序计算周长。
图1
如图1所示7个矩形。
图2
如图2所示,所有矩形的边界。所有矩形顶点的坐标都是整数。

输入格式:

输入文件的第一行是一个整数N(0<=N<5000),表示有多少个矩形。接下来N行给出了每一个矩形左下角坐标和右上角坐标(所有坐标的数值范围都在-10000到10000之间)。

输出格式:

输出文件只有一个正整数,表示所有矩形的周长。


题解:

本蒟蒻第一眼看到这道题,跟线段树有什么关系???
好吧,其实是可以暴力做出来的,但是一点也不优秀!!!
所以我们来想一下线段树的做法
预处理:把每一个矩形的两个长和两个宽记录在数组里,并且要记录每一条边的类型。
每个边的类型有两种,矩形的左边与下边记为类型一,矩形的右边和上边记为类型二.
把每个垂直于X轴的边按横坐标从左往右排序,每一个平行于X轴的边按纵坐标从下往上排序。
但是,坑点来了!!当两个边的位置相同时,要把一类边放在前面!(之后讲原因)
将两个坐标轴分别看成两个线段树,Y轴向右平移,X轴向上平移(负数坐标只需要整体+10000)
在线段树里维护一个cover值,其中cover[i]表示从i.l到i.r这个区间被完全覆盖的次数
当扫描到一条线段时,有两种情况:
1.这是一个一类线段,那么就将这条线段对应的区间的cover值+1.
这个线段对总周长做出的贡献就是插入前被覆盖区间的总长A1减去插入后被覆盖区间的总长B1的绝对值。
2.这是一个二类线段,那么就将这条线段对应的区间的cover值-1.
同理,这个线段对总周长做出的贡献就是插入前被覆盖区间的总长A1减去插入后被覆盖区间的总长B1的绝对值。
如何算出当前被覆盖区间的总长呢?可以对整个线段树做一次查询,对于一个cover值不为0的区间,说明他已经被完全覆盖了,所以被覆盖区间的总长就加上(l-r),找到一个完全覆盖的区间之后便不往这个区间的下层查询了,因为很明显这样会算重。
但是为什么要把一类边放在前面呢?考虑这样一种情况:
这里写图片描述
一个一类边和一个二类边重合了。这个时候,我们不能先算二类边,因为在处理下一条一类边的时候会再重复加一次!


贴上代码:(本蒟蒻用了动态开点)

#include<stdio.h>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<memory.h>
#define maxn 80005
#define maxm 5005
using namespace std;
struct line
{
    int x,y,pos,type;
}ver[maxm*2],hor[maxm*2];
int n,cnt,ans,tot=1;
int ls[maxn],rs[maxn],cover[maxn];
int getsum(int p,int l,int r)
{
    if(cover[p]!=0) return (r-l);
    if((r-l==1)||((!ls[p])&&(!rs[p]))) return 0;
    int mid=(l+r)>>1,ret=0;
    if(!ls[p]) ls[p]=++tot;
    if(!rs[p]) rs[p]=++tot;
    ret+=getsum(ls[p],l,mid);
    ret+=getsum(rs[p],mid,r);
    return ret;
}
void modify(int p,int l,int r,int x,int y,int val)
{
    if(x<=l&&r<=y)
    {
        cover[p]+=val;
        return;
    }
    int mid=(l+r)>>1;
    if(x<mid&&y>=l)
    {
        if(!ls[p]) ls[p]=++tot;
        modify(ls[p],l,mid,x,y,val);
    }
    if(y>mid&&x<=r)
    {
        if(!rs[p]) rs[p]=++tot;
        modify(rs[p],mid,r,x,y,val);
    }
}
bool cmp(line a,line b)
{
    if(a.pos==b.pos) return a.type<b.type;
    return a.pos<b.pos;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int x,y,x1,y1;
        scanf("%d%d%d%d",&x,&y,&x1,&y1);
        x+=10001,y+=10001,x1+=10001,y1+=10001;
        ver[++cnt].x=y1;
        ver[cnt].y=y;
        ver[cnt].pos=x;
        ver[cnt].type=1;
        //right line init
        hor[cnt].x=x1;
        hor[cnt].y=x;
        hor[cnt].pos=y;
        hor[cnt].type=1;
        //lower line init
        ver[++cnt].x=y1;
        ver[cnt].y=y;
        ver[cnt].pos=x1;
        ver[cnt].type=2;
        //left line init
        hor[cnt].x=x1;
        hor[cnt].y=x;
        hor[cnt].pos=y1;
        hor[cnt].type=2;
        //upper line init
    }
    sort(hor+1,hor+2*n+1,cmp);
    sort(ver+1,ver+2*n+1,cmp);
    for(int i=1;i<=2*n;i++)
    {
        if(ver[i].type==1)
        {
            int s1=getsum(1,1,maxn),s2;
            modify(1,1,maxn,ver[i].y,ver[i].x,1);
            s2=getsum(1,1,maxn);
            ans+=abs(s1-s2);
        }
        else
        {
            int s1=getsum(1,1,maxn),s2;
            modify(1,1,maxn,ver[i].y,ver[i].x,-1);
            s2=getsum(1,1,maxn);
            ans+=abs(s1-s2);
        }
    }
    memset(cover,0,sizeof(cover));
    memset(ls,0,sizeof(ls));
    memset(rs,0,sizeof(rs));
    tot=1;
    for(int i=1;i<=2*n;i++)
    {
        if(hor[i].type==1)
        {
            int s1=getsum(1,1,maxn),s2;
            modify(1,1,maxn,hor[i].y,hor[i].x,1);
            s2=getsum(1,1,maxn);
            ans+=abs(s1-s2);
        }
        else
        {
            int s1=getsum(1,1,maxn),s2;
            modify(1,1,maxn,hor[i].y,hor[i].x,-1);
            s2=getsum(1,1,maxn);
            ans+=abs(s1-s2);
        }
    }
    printf("%d",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值