HDU - 1255 覆盖的面积 【线段树求相交面积】

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1255

这题的方法在之前的博客有比较详细的描述,这里把需要注意的重点说一下就行了。

https://blog.csdn.net/godleaf/article/details/86608855

如果题目只是问覆盖至少一次的话,代码基本上不用变,只需要把ans累加的公式改一下就行了,\Delta X就是两个相邻的垂直线距离,y就是被覆盖的y轴长度。

但是题目要求的是至少2次,那么代码不是说把 cover > 0 改成 cover > 1就行了。 之前的博客有说到cover标记的可信问题。如果cover > 0,那么当前区间就一定能整段取, == 0则不能保证,需要访问左右区间才能知道被覆盖1次的区间长度有多少。如果求覆盖至少2次,那么cover > 1就是可信的,能够整段取,== 0 的情况也能通过左右区间得到覆盖至少2次的区间长度,但是 == 1还存在一种情况,如果当前cover == 1,而子孙节点有 == 1的情况,那么这个子孙节点的区间也是可以取的,但是我们记录了覆盖至少2次的区间长度,当前节点并不知道自己的子孙节点被覆盖1次的区间有多少。讲到这里大概都能猜出怎么做了吧。就是在该线段树扩展一个记录覆盖1次的区间长度,这样就能解决问题了。

在pushup更新区间长度的时候一定要注意不要重复了。当cover == 1 的时候,被覆盖至少两次的长度就是被覆盖2次的长度+被覆盖1次的长度,那么当前区间被覆盖1次的长度就不是一整段区间了,因为有一部分已经加到被覆盖2次的区间长度里面了,所以要除去这些加到被覆盖2次的区间长度。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <set>

using namespace std;

#define lc (cur<<1)
#define rc ((cur<<1)|1)

const int Maxn = 1e3+10;
const int mod = 10007;

typedef long long ll;

struct tree {
    int cover;
    double len1, len2;
} seg[Maxn<<5];

struct Edge {
    int l ,r, val;
    double od;
    bool operator < (const Edge &a1) const {
        return od < a1.od;
    }
} edge[Maxn<<1];

double Hash[Maxn<<2], tmp[Maxn<<2];
int L, R, op;

void pushup(int cur, int l, int r) {
    if(seg[cur].cover > 1) {
        seg[cur].len2 = Hash[r]-Hash[l-1];
        seg[cur].len1 = 0;
    }

   else if(seg[cur].cover == 1) {
        if(l == r) {
            seg[cur].len2 = 0;
            seg[cur].len1 = Hash[r]-Hash[l-1];
        }
        else {
            seg[cur].len2 = seg[lc].len1+seg[rc].len1+seg[lc].len2+seg[rc].len2;
            seg[cur].len1 = Hash[r]-Hash[l-1]-seg[cur].len2;
        }
    }
    else if(seg[cur].cover == 0) {
        if(l == r) seg[cur].len1 = seg[cur].len2 = 0;
        else {
            seg[cur].len1 = seg[lc].len1+seg[rc].len1;
            seg[cur].len2 = seg[lc].len2+seg[rc].len2;
        }
    }
}

void updata(int cur, int l, int r) {
    if(L <= l && r <= R) {
        seg[cur].cover += op;
        pushup(cur, l, r);
        return;
    }

    int mid = (l+r)>>1;

    if(L <= mid) updata(lc, l, mid);
    if(mid+1 <= R) updata(rc, mid+1, r);

    pushup(cur, l, r);
}

int main(void)
{
    int T, N;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &N);
        for(int i = 0; i < 4*N; ++i) {
            scanf("%lf", &tmp[i]);
            Hash[i] = tmp[i];
        }

        sort(Hash, Hash+4*N);
        int len = unique(Hash, Hash+4*N)-Hash, m = 0;

        for(int i = 0; i < N; ++i) {
            int l = lower_bound(Hash, Hash+len, tmp[(i<<2)+1])-Hash+1;
            int r = lower_bound(Hash, Hash+len, tmp[(i<<2)+3])-Hash;

            edge[++m].l = l; edge[m].r = r; edge[m].od = tmp[(i<<2)];
            edge[m].val = 1;
            edge[++m].l = l; edge[m].r = r; edge[m].od = tmp[(i<<2)+2];
            edge[m].val = -1;
        }

        sort(edge+1, edge+m+1);

        for(int i = 0; i <= 4*len; ++i) seg[i].cover = seg[i].len1 = seg[i].len2 = 0;

        double ans = 0;
        for(int i = 1; i < m; ++i) {
            L = edge[i].l; R = edge[i].r;
            op = edge[i].val;
            updata(1, 1, len-1);
            ans += (seg[1].len2*(edge[i+1].od-edge[i].od));
        }
        printf("%.2f\n", ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值