【题】【线段树(lazy)】NKOJ 1868 矩形周长【USACO5.5.1】Picture

NKOJ 1868 【USACO5.5.1】Picture矩形周长
时间限制 : 10000 MS 空间限制 : 65536 KB
问题描述
N(N<5000) 张矩形的海报,照片和其他同样形状的图片贴在墙上。它们的边都是垂直的或水平的。每个矩形可以部分或者全部覆盖其他矩形。所有的矩形组成的集合的轮廓称为周长。写一个程序计算周长。
图 1 是一个有 7 个矩形的例子:
这里写图片描述
图 1.一个 7 个矩形的集合
对应的轮廓为图 2 所示的所有线段的集合:
这里写图片描述
图 2. 矩形集合的轮廓
所有矩形的顶点坐标均为整数。所有的坐标都在 [-10000,10000] 的范围内,并且任何一个矩形面积都为整数。结果的值可能需要 32 位有符号整数表示。

输入格式
第1行: N,张贴在墙上的矩形的数目。 第 2..N+1行 接下来的N行中,每行都有两个点的坐标,分别是矩形的左下角坐标和右上角坐标。每一个坐标由 X 坐标和 Y 坐标组成。

输出格式
只有一行,为一个非负整数,表示输入数据中所有矩形集合的轮廓长度。

样例输入
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

样例输出
228

思路:
1、将所有矩形看作四条边
2、对于每条横向边,按纵坐标排序,依次讨论,若讨论的为某个矩形靠下的一条边,ans+=将这条边覆盖在线段树上前后,该边覆盖区间内被覆盖数之差。若为靠上的一条边ans+=将这条边之前的一条对应边删除前后,该边覆盖区间内被覆盖数之差。
3、lazy表示覆盖的状态。

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstdlib>
using namespace std;
const int need=20003;

struct yf{int x1,y1,x2,y2;short cnt;}sq[10003];
struct fy{int a,b,lazy;}t[need*2];//
int le[need*2],ri[need*2];

int tot=0,x,y;

bool cy(yf a,yf b)
{
    int s1=a.cnt==1?a.y1:a.y2,s2=b.cnt==1?b.y1:b.y2;
    return s1<s2;
}
bool cx(yf a,yf b)
{
    int s1=a.cnt==1?a.x1:a.x2,s2=b.cnt==1?b.x1:b.x2;
    return s1<s2;
}

void build(int x,int y)
{
    int s=++tot;
    t[s].a=x,t[s].b=y;
    if(x+1==y) return ;
    le[s]=tot+1;build(x,(x+y)>>1);
    ri[s]=tot+1;build((x+y)>>1,y);
}

void putdown(int s)
{
    if(t[s].a+1==t[s].b) return ;
    int k=t[s].lazy;
    t[s].lazy=0;
    t[le[s]].lazy+=k;
    t[ri[s]].lazy+=k; 
}
void cover_(int s)
{
    if(x>t[s].b||y<t[s].a) return ;
    if(t[s].lazy!=0) putdown(s);
    if(x<=t[s].a&&t[s].b<=y) 
    {
        t[s].lazy++;
        return ;
    }
    cover_(le[s]),cover_(ri[s]);
}
void clean_(int s)
{
    if(x>t[s].b||y<t[s].a) return ;
    if(t[s].lazy!=0) putdown(s);
    if(x<=t[s].a&&t[s].b<=y) 
    {
        t[s].lazy--;
        return ;
    }
    clean_(le[s]),clean_(ri[s]);
}

int cnt_(int s)
{
    if(x>t[s].b||y<t[s].a) return 0;
    if(x<=t[s].a&&t[s].b<=y&&t[s].lazy>0) return t[s].b-t[s].a;
    if(t[s].lazy!=0) putdown(s);
    return cnt_(le[s])+cnt_(ri[s]);
}

int main()
{
    build(1,20000);
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&sq[i].x1,&sq[i].y1,&sq[i].x2,&sq[i].y2);
        sq[i].x1+=10000,sq[i].y1+=10000,sq[i].x2+=10000,sq[i].y2+=10000;
        sq[i].cnt=1;
        sq[i+n]=sq[i];
        sq[i+n].cnt=2;
    }
    sort(sq+1,sq+1+(n<<1),cy);
    int ans=0,cnt1,cnt2;
    for(int i=1,nn=n<<1;i<=nn;i++)
    {
        x=sq[i].x1,y=sq[i].x2;
        if(sq[i].cnt==1)
        {
            cnt1=cnt_(1);
            cover_(1);
            cnt2=cnt_(1);
            ans+=abs(cnt1-cnt2);
        }
        if(sq[i].cnt==2)
        {
            cnt1=cnt_(1);
            clean_(1);
            cnt2=cnt_(1);
            ans+=abs(cnt1-cnt2);
        }
    }
    for(int i=1;i<=need*2;i++) t[i].lazy=0;
    sort(sq+1,sq+1+(n<<1),cx);
    for(int i=1,nn=n<<1;i<=nn;i++)
    {
        x=sq[i].y1,y=sq[i].y2;
        if(sq[i].cnt==1)
        {
            cnt1=cnt_(1);
            cover_(1);
            cnt2=cnt_(1);
            ans+=abs(cnt1-cnt2);
        }
        if(sq[i].cnt==2)
        {
            cnt1=cnt_(1);
            clean_(1);
            cnt2=cnt_(1);
            ans+=abs(cnt1-cnt2);
        }
    }
    printf("%d",ans);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值