hdu1828 Picture(扫描线求矩形周长并)

题目链接

分析:
从左往右在每一次插入一条边后,周长并的累加值=新增的竖边+新增的横边

原题重做
代码重构了一下,这样就和求矩阵面积并的代码统一起来了(好记)


随意看一下代码:

我们是在y坐标上建立线段树:
在处理矩形信息的时候,我们拆成两个操作
对于x坐标相同的操作,我们先插入再删除(处理重边)

int cmp(const point &a,const point &b) {              //先插入后删除 
    return (a.x<b.x)||(a.x==b.x&&a.type>b.type);
}

int xa,ya,xb,yb;
for (int i=1;i<=n;i++) {
    scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
    tot++; Y[tot]=ya;
    a[tot].x=xa; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=1;
    tot++; Y[tot]=yb;
    a[tot].x=xb; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=-1;
}
struct node{
    int l,r,ml,mr,len;
    int cover,lp,rp,cnt;
}t[N<<2];

线段树中维护:
l,r: l , r : 线段树中的左右端点
ml,mr: m l , m r : 该结点维护的真实区间长度
len: l e n : 该区间中被覆盖的线段长度
cover: c o v e r : 覆盖标记
lp,rp: l p , r p : 左右端点是否被覆盖
cnt: c n t : 该区间会产生多少水平线

在统计答案时候,对于竖线,我们只统计增量abs(delta-t[1].len)

ll ans=0;
int delta=0;
for (int i=1;i<=tot;i++) {
    add(1,a[i]);
    ans+=(ll)abs(delta-t[1].len);
    delta=t[1].len;
    if (i!=tot) ans+=(ll)(a[i+1].x-a[i].x)*t[1].cnt;
}
printf("%lld\n",ans);

因为图形多半是在平面直角坐标系上的,所以扫描线的build函数有一点不同
这种build树方式,会影响到之后的add和update

void build(int bh,int l,int r) {
    t[bh].l=l; t[bh].r=r;
    t[bh].ml=Y[l]; t[bh].mr=Y[r];
    t[bh].lp=t[bh].rp=t[bh].cnt=t[bh].len=0;
    if (l+1==r) return;
    int mid=(l+r)>>1;
    build(bh<<1,l,mid);     //mid
    build(bh<<1|1,mid,r);   //mid
}

插入操作的时候,我们标记永久化(毕竟我们只关心t[1]的信息,update就好了)
注意我们在完全覆盖之后,要update一下

在把区间分给两个儿子的时候,注意范围的判断a.yr<=t[bh<<1].mr,t[bh<<1|1].ml<=a.yl(有等号)
如果区间跨越两个儿子,我们就用下面的方式分到两个儿子中
(在求矩形面积并时也是这样处理的)

void add(int bh,point a) {
    if (t[bh].ml==a.yl&&t[bh].mr==a.yr) {
        t[bh].cover+=a.type;
        update(bh);    //维护一下
        return;
    }
    if (a.yr<=t[bh<<1].mr) add(bh<<1,a);
    else if (t[bh<<1|1].ml<=a.yl) add(bh<<1|1,a);
    else {
        point v=a;
        v.yr=t[bh<<1].mr;
        add(bh<<1,v);
        v=a;
        v.yl=t[bh<<1|1].ml;
        add(bh<<1|1,v);
    }
    update(bh);
}

最后就是我们最重要的update
如果完全覆盖,没什么可说的
不过如果没有覆盖标记又是叶子结点,我们要记得把ta的标记都清空
(没有这个处理,好像在多组数据的情况下会发生一些意外,一切以稳为第一要义
如果当前结点的信息需要从儿子维护上来,注意特判一下cnt

void update(int bh) {
    int lc=bh<<1;
    int rc=bh<<1|1;
    if (t[bh].cover>0) {
        t[bh].len=t[bh].mr-t[bh].ml;
        t[bh].lp=t[bh].rp=1;
        t[bh].cnt=2;
    }
    else if (t[bh].l+1==t[bh].r){          //important
        t[bh].len=0;
        t[bh].lp=t[bh].rp=t[bh].cnt=0;
    }
    else {
        t[bh].len=t[lc].len+t[rc].len;
        t[bh].lp=t[lc].lp;
        t[bh].rp=t[rc].rp;
        t[bh].cnt=t[lc].cnt+t[rc].cnt;
        if (t[lc].rp&&t[rc].lp) t[bh].cnt-=2;
    }
}

tip

多组数据
注意初始化

我们的口号是:稳是第一要义
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long

using namespace std;

const int N=10010;
struct node{
    int l,r,ml,mr,len;
    int cover,lp,rp,cnt;
}t[N<<2];
struct point{
    int x,yl,yr,type;
}a[N];
int n,Y[N],tot,nn;

int cmp(const point &a,const point &b) {              //先插入后删除 
    return (a.x<b.x)||(a.x==b.x&&a.type>b.type);
}

void build(int bh,int l,int r) {
    t[bh].l=l; t[bh].r=r;
    t[bh].ml=Y[l]; t[bh].mr=Y[r];
    t[bh].lp=t[bh].rp=t[bh].cnt=t[bh].len=0;
    if (l+1==r) return;
    int mid=(l+r)>>1;
    build(bh<<1,l,mid);
    build(bh<<1|1,mid,r);  
}

void update(int bh) {
    int lc=bh<<1;
    int rc=bh<<1|1;
    if (t[bh].cover>0) {
        t[bh].len=t[bh].mr-t[bh].ml;
        t[bh].lp=t[bh].rp=1;
        t[bh].cnt=2;
    }
    else if (t[bh].l+1==t[bh].r){
        t[bh].len=0;
        t[bh].lp=t[bh].rp=t[bh].cnt=0;
    }
    else {
        t[bh].len=t[lc].len+t[rc].len;
        t[bh].lp=t[lc].lp;
        t[bh].rp=t[rc].rp;
        t[bh].cnt=t[lc].cnt+t[rc].cnt;
        if (t[lc].rp&&t[rc].lp) t[bh].cnt-=2;
    }
}

void add(int bh,point a) {
    if (t[bh].ml==a.yl&&t[bh].mr==a.yr) {
        t[bh].cover+=a.type;
        update(bh);
        return;
    }
    if (a.yr<=t[bh<<1].mr) add(bh<<1,a);
    else if (t[bh<<1|1].ml<=a.yl) add(bh<<1|1,a);
    else {
        point v=a;
        v.yr=t[bh<<1].mr;
        add(bh<<1,v);
        v=a;
        v.yl=t[bh<<1|1].ml;
        add(bh<<1|1,v);
    }
    update(bh);
}

int main()
{
    while (scanf("%d",&n)!=EOF) {
        memset(Y,0,sizeof(Y));
        tot=0;                     //初始化啊

        int xa,ya,xb,yb;
        for (int i=1;i<=n;i++) {
            scanf("%d%d%d%d",&xa,&ya,&xb,&yb);
            tot++; Y[tot]=ya;
            a[tot].x=xa; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=1;
            tot++; Y[tot]=yb;
            a[tot].x=xb; a[tot].yl=ya; a[tot].yr=yb; a[tot].type=-1;
        }

        sort(Y+1,Y+1+tot);
        nn=unique(Y+1,Y+1+tot)-Y-1;
        build(1,1,nn);

        sort(a+1,a+1+tot,cmp);
        ll ans=0;
        int delta=0;
        for (int i=1;i<=tot;i++) {
            add(1,a[i]);
            ans+=(ll)abs(delta-t[1].len);
            delta=t[1].len;
            if (i!=tot) ans+=(ll)(a[i+1].x-a[i].x)*t[1].cnt;
        }
        printf("%lld\n",ans);
    }   
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值