hdu1828,扫描线求矩形边长

这道题利用扫描线求取矩形的周长,不包括重叠部分。可以扫两次,竖着扫一次,横着扫一次,扫两次这个我写出bug来了,现在也不知道bug在哪儿QAQ,反手一想算了还是去写扫一次的。
在这里插入图片描述
画图模拟一下扫描线扫的过程会发现我们横着的周长就是每加入一条扫描线后线段树总长的变化,而竖着的则和我们线段树维护的被覆盖的区间是几段有关,设是x段,那么本次竖着的周长=(下一条扫描线高度 - 这一条扫描线的高度) * x * 2。所以我们要维护加入扫描线后整个区间的长度,和这个长度由几段组成。
说一下维护这个段数,对于一个区间,我们要知道他有几段,就要知道它两个儿子有多少段,并且由于儿子的合并,可能会导致两段合并成一段,我们每个区间维护两个值fl,fr表示这个区间的左半部分和有伴部分是否已经被覆盖,如果左儿子的右部分和右儿子的左部分都被覆盖(如下)那么father的段数 = 儿子的段数之和 - 1,中间部分合并了所以段数少1,其他情况 = 儿子的段数之和。最后得出答案就是
在这里插入图片描述

#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<cstdio>
#define mod (10007)
#define middle (l+r)>>1
#define SIZE 1000000+5
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double ld;
const int inf_max = 0x3f3f3f;
const ll Linf = 9e18;
const int maxn = 2e4 + 5;
const long double E = 2.7182818;
const double eps=0.0001;
using namespace std;
inline int read()
{
    int f=1,res=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { res=res*10+ch-'0' ; ch=getchar(); }
    return f*res;
}
const int up = 100000 + 10;
struct node {
    int l,r,val,h;
    node(){};
    node(int x,int y,int z,int flag) {l = x;r = y;h = z;val = flag;}
}Line[maxn];
struct tnode {
    int flag,segnum,len,fl,fr;
}tree[up << 2];
int n,totx;
bool cmp(node a,node b) {
    return (a.h == b.h ? a.val > b.val : a.h < b.h);
}
void rst() {
    totx = 0;
    memset(tree,0,sizeof(tree));
}
void pushup(int rt,int l,int r) {
    if(tree[rt].flag) { //如果这一整段区间都被覆盖,那么就不用管儿子,直接自己更形
        tree[rt].len = r + 1 - l;
        tree[rt].fl = 1; tree[rt].fr = 1; tree[rt].segnum = 1;
    }else {
        tree[rt].len = tree[lson].len + tree[rson].len;
        tree[rt].segnum = tree[rson].segnum + tree[lson].segnum;
        if(tree[lson].fr == 1 && tree[rson].fl == 1) tree[rt].segnum--; //如何左儿子和右儿子中间部分可以合为一段
        tree[rt].fl = tree[lson].fl;tree[rt].fr = tree[rson].fr;
    }
}
void update(int rt,int l,int r,int ql,int qr,int f) {
    if(ql == l && qr == r) {
        tree[rt].flag += f;
        pushup(rt,l,r);
        return ;
    }
    int mid = middle;
    if(qr <= mid) update(lson,l,mid,ql,qr,f);
    else if(ql > mid) update(rson,mid + 1,r,ql,qr,f);
    else {
        update(lson,l,mid,ql,mid,f);
        update(rson,mid + 1,r,mid + 1,qr,f);
    }
    pushup(rt,l,r);
}
int main()
{
    while(~scanf("%d",&n)) {
        rst();
        for(int i = 1;i <= n; ++i) {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            x1 += 10005,y1 += 10005,x2 += 10005,y2 += 10005; //省去离散化
            Line[++totx] = node(x1,x2,y1,1);
            Line[++totx] = node(x1,x2,y2,-1);
        }
        sort(Line + 1,Line + 1 + totx,cmp);
        int ans = 0,pre = 0;
        for(int i = 1;i <= totx; ++i) {
            update(1,1,up,Line[i].l,Line[i].r - 1,Line[i].val); //把点换成区间,所以右端点要-1;
            if(i != totx)
                ans += (2 * tree[1].segnum * (Line[i + 1].h - Line[i].h)); //计算竖着的
            ans += abs(pre - tree[1].len);//计算横着的
            pre = tree[1].len;
        }
        cout<<ans<<endl;
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值