HDU1255 覆盖的面积 (线段树 + 扫描线)

题目链接:覆盖的面积

大致题意

现在有平面直角坐标系xoy, 有n个平行于x轴y轴的矩形, 给出这些矩形的左上角和右下角的坐标, 让你求出这些矩形组成的图形中被至少两个矩形覆盖的部分的总面积.

解题思路

如果你还没有做过这道题, 推荐点击链接先看一下这个题

这个题和上面给出超链接的题是几乎一模一样的, 我在这里不再多分析了, 唯一的区别就是要求计算至少被两个矩形的面积.

设当前区间被覆盖一次的长度为len1, 两次为len2, 那么我们类比于上一题的思路, 我们从小到大枚举x, 最终的结果只需要累加△x * len2即可.

对于len1, 我们维护的策略与上一题的len是相同的. 那么我们只需要考虑如何维护len2即可.

首先对于任意的区间 一定满足: len2 >= len1, 记为¥式

​ ①如果这个区间被覆盖>=2次, 则len2 = 区间内线段的长度.
​ ②如果这个区间恰好被覆盖一次, 由于我们的计数不向下传递, 其实相当于其子节点也都会多被覆盖一次, 因此 len2 = left_son.len1 + right_son.len1.
​ ③如果这个区间没有被覆盖过, 则len2 = left_son.len2 + right_son.len2.

对于②和③的顺序, 不可以颠倒, 因为如¥式所述, ②所得到的区间一定是包含③所得到的, 因此应当先计算大的区间.

AC代码(注释同上一题代码, 如有需要请点击上文超链接查看)

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E3 + 10;
vector<double> v;
int find(double x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); }
struct LINE {
    double x, y1, y2; int c;
    bool operator< (const LINE& t) const { return x < t.x; }
}; vector<LINE> line;


struct node {
    int l, r;
    int cou;
    double len1, len2; //覆盖1次, 覆盖2次.
}t[N << 3];
double getlen(int l, int r) { return v[r + 1] - v[l]; }
void pushup(int x) {
    if (t[x].cou) t[x].len1 = getlen(t[x].l, t[x].r);
    else if (t[x].l != t[x].r) t[x].len1 = t[x << 1].len1 + t[x << 1 | 1].len1;
    else t[x].len1 = 0;
    
    if (t[x].cou >= 2) t[x].len2 = getlen(t[x].l, t[x].r);
    else if (t[x].cou) t[x].len2 = t[x << 1].len1 + t[x << 1 | 1].len1;
    else if (t[x].l != t[x].r) t[x].len2 = t[x << 1].len2 + t[x << 1 | 1].len2;
    else t[x].len2 = 0;
}

void build(int l, int r, int x = 1) {
    t[x] = { l, r, 0, 0 };
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
}

void modify(int l, int r, int c, int x = 1) {
    if (l <= t[x].l && r >= t[x].r) {
        t[x].cou += c;
        pushup(x);
        return;
    }
    int mid = t[x].l + t[x].r >> 1;
    if (l <= mid) modify(l, r, c, x << 1);
    if (r > mid) modify(l, r, c, x << 1 | 1);
    pushup(x);
}
int main()
{
    int T; cin >> T;
    while (T--) {
        int n; scanf("%d", &n);
        
        v.clear(); line.clear(); v.push_back(-0x3f3f3f3f);
        rep(i, n) {
            double x1, y1, x2, y2; scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
            line.push_back({ x1, y1, y2, 1 });
            line.push_back({ x2, y1, y2, -1 });
            v.push_back(y1), v.push_back(y2);
        }
        sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end());
        build(1, v.size() - 2);
        
        sort(line.begin(), line.end());
        
        double res = 0, last = 0;
        for (auto& op : line) {
            res += (op.x - last) * t[1].len2;
            last = op.x;
            modify(find(op.y1), find(op.y2) - 1, op.c);
        }
        printf("%.2f\n", res);
    }
    return 0;
}
kuangbin线段树专题点这里!!!

END

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值