hdu1828,扫描线求矩形边长

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

#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;
}
hdu1828,扫描线求矩形边长hdu1828,扫描线求矩形边长 Alstein 发布了37 篇原创文章 · 获赞 19 · 访问量 6195 私信 关注
上一篇:CF1000F One Occurrence 主席树 线段树


下一篇:CF380C Sereja and Brackets 括号序列+线段树