POJ 3277 线段树 + 延迟标记

本文介绍了一种利用线段树解决计算多个矩形非重叠总面积的问题。通过特殊的更新与查询策略,确保了即使在矩形部分重叠的情况下也能正确计算总面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


本题就是给出一堆矩形,然后让我们算总共的面积,也就是重复的面积只算一次。需要用到线段树,每次输入矩形的左端点和右端点,还有高。我们就把这条线段插进去,线段的高度取较高的那一个。注意到这和普通的线段树不太一样,普通的情况是,当我们插入的线段完全覆盖线段树中的某一条线段时,那条线段的值就被更新成插入的线段的值。但是本题的情况却不太一样,当我们插入的线段完全覆盖线段树的某一条线段时,如果现在的权值大于原有线段的权值,就将其覆盖。但是一定要注意,更新之后的线段树的值并不一定是正确的。

比如我们最先插入(1,5),5。然后打上延迟标记。第二次插入(1,3),7。所以我们就把延迟标记下移。(1,3)和(3,5)都更新成5,然后(1,3)变成7。然后再把(1,5)更新成6。现在假如我们来查询,难道(1,5)的值就是6吗?明显不是,(1,3)区间的权值是7,(3,5)区间的值为5。那么怎么才能查询到正确的结果呢?只需要从根节点,不断的往叶子节点更新就行了。


所以这道题:


1.更新的时候延迟标记下移的规则不是完全覆盖,是取一个区间中的最大值


2.查询的时候,从根节点往叶子节点更新


3.本题线段树叶子节点应该构建成(x,x + 1)的形式,不然会漏掉[m,m + 1]的面积

#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <complex>
#include <cstring>
#include <utility>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <string>
#include <cctype>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <new>
#include <set>
#include <map>
#define lson l, m, k << 1
#define rson m, r, k << 1| 1

using namespace std;

typedef long long int LL;
const int INF = 0x3f3f3f3f;
const int maxn = 200000 << 2;
LL cover[maxn], maxh[maxn], li[maxn], ri[maxn], hi[maxn], x[maxn];

void pushdown(int k){
    if (cover[k]){
        if (maxh[k << 1] < maxh[k]){
            maxh[k << 1] = maxh[k];
            cover[k << 1] = 1;
        }
        if (maxh[k << 1 | 1] < maxh[k]){
            maxh[k << 1 | 1] = maxh[k];
            cover[k << 1 | 1] = 1;
        }
        cover[k] = 0;
    }
}

void update(int l, int r, int k, int L, int R, int H){
    if (L == l && R == r){
        if (maxh[k] < H){
            maxh[k] = H;
            cover[k] = 1;
        }
        return;
    }
    pushdown(k);
    int m = (l + r) >> 1;
    if (R <= m) update(lson, L, R, H);
    else if (L >= m) update(rson, L, R, H);
    else{
        update(lson, L, m, H);
        update(rson, m, R, H);
    }
}

LL query(int l, int r, int k){
    if (l == r - 1) return maxh[k] * (x[r] - x[l]);
    maxh[k << 1] = max(maxh[k], maxh[k << 1]);
    maxh[k << 1 | 1] = max(maxh[k], maxh[k << 1 | 1]);
    int m = (l + r) >> 1;
    return query(lson) + query(rson);
}

int Binary_Search(int s, int e, int key) {
    int left = s, right = e, mid;
    while (left <= right) {
        mid = (left + right) >> 1;
        if (x[mid] >= key)  right = mid - 1;
        else left = mid + 1;
    }
    if (x[left] == key) return left;
    return -1;
}

int main()
{
    //freopen("1.txt", "r", stdin);
    int n;
    scanf ("%d", &n);
    int nn = 0;
    for (LL i = 1; i <= n; i++) {
        scanf ("%I64d%I64d%I64d", &li[i], &ri[i], &hi[i]);
        x[++nn] = li[i];
        x[++nn] = ri[i];
    }
    
    memset(maxh, 0, sizeof(maxh));//建树
    memset(cover, 0, sizeof(cover));

    sort(x + 1, x + nn + 1);//离散化
    int len = unique(x + 1, x + nn + 1) - (x + 1);
    for (LL i = 1; i <= n; i++) {
        LL l = Binary_Search(1, len + 1, li[i]);
        LL r = Binary_Search(1, len + 1, ri[i]);
        update(1, len, 1, l, r, hi[i]);
    }
    
    printf("%I64d\n", query(1, len, 1));
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值