洛谷P1502 窗口的星星(思维,扫描线)

这篇博客主要介绍了如何使用扫描线算法解决二维平面上矩形区域权值最大值的问题。通过将矩形范围稍作调整,将开区间转换为闭区间,然后进行离散化处理,结合线段树进行区间加和和区间最大值查询。文章通过C++代码展示了具体的实现过程,包括线段树的构建和更新,以及如何通过扫描线找到最大权值。

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

P1502 窗口的星星

原题链接

在这里插入图片描述
对于一颗星星 ( x , y ) (x,y) (x,y) ,考虑要包含这颗星星,窗口右端点的范围,实际上是一个矩形: ( ( x ∼ x + w ) , ( y ∼ y + h ) ) ((x∼x+w),(y∼y+h)) ((xx+w),(yy+h)) ,因为不能在边框上,所以都是开区间。那么实际上就是每个点都给一块矩形加上了 l i l_i li 的权值,然后求权值最大的点的权值是多少。一看,实际上是遍历了所有矩形的并集,那么就想到用扫描线去解决。但是这里的开区间不好处理,所以我们可以把这个矩形的范围替换成这样: ( ( x + 0.5 ∼ x + w − 0.5 ) , ( y + 0.5 ∼ y + h − 0.5 ) ) ((x+0.5∼x+w-0.5),(y+0.5∼y+h-0.5)) ((x+0.5x+w0.5),(y+0.5y+h0.5)) 。我们可以发现,这样替换之后,并未对矩形的并集造成影响。所以我们处理出每个矩形,进行离散化后做扫描线,线段树就只有区间加和询问区间最大值两个操作,而且区间最大值也就是根节点,不用再单独写函数询问。这里的扫描线实际上就是以不断以某区域的底边为代表,询问了区间最大值,实际上询问了一个二维平面的最大值。

#include <bits/stdc++.h>
#define lson rt<<1
#define rson (rt<<1)|1

using namespace std;

const int N = 1e4 + 5;

typedef long long ll;

struct line {
    double l; double r; double h; ll v;
};
vector<line> seg;

int n, w, h, tot;

ll tree[N << 4], lz[N << 4];

double x[N << 1];

bool cmp(line a, line b) {
    if (a.h == b.h) return a.v > b.v;
    return a.h < b.h;
}

void push_down(int rt) {
    if (!lz[rt]) return ;
    lz[lson] += lz[rt]; lz[rson] += lz[rt];
    tree[lson] += lz[rt]; tree[rson] += lz[rt];
    lz[rt] = 0;
}

void update(int rt, int l, int r, int L, int R, ll v) {
    if (L <= l && r <= R) {
        tree[rt] += v;
        lz[rt] += v;
        return ;
    }
    push_down(rt);
    int mid = l + r >> 1;
    if (mid >= L) update(lson, l, mid, L, R, v);
    if (mid < R) update(rson, mid + 1, r, L, R, v);
    tree[rt] = max(tree[lson], tree[rson]);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T;
    scanf("%d", &T);
    while(T--) {
        memset(tree, 0, sizeof tree);
        memset(lz, 0, sizeof lz);
        tot = 0;
        seg.clear();
        scanf("%d%d%d", &n, &w, &h);
        for (int i = 1, xx, yy, ll; i <= n; ++i) {
            scanf("%d%d%d", &xx, &yy, &ll);
            double L = xx + 0.5, R = xx + w - 0.5;
            x[++tot] = L;
            x[++tot] = R;
            seg.push_back({L, R, yy + 0.5, ll});
            seg.push_back({L, R, yy + h - 0.5, -ll});
        }
        sort(x + 1, x + 1 + tot);
        sort(seg.begin(), seg.end(), cmp);
        tot = unique(x + 1, x + 1 + tot) - x - 1;
        ll ans = 0;
        for (int i = 0; i < (int)seg.size(); ++i) {
            int L = lower_bound(x + 1, x + 1 + tot, seg[i].l) - x;
            int R = lower_bound(x + 1, x + 1 + tot, seg[i].r) - x;
            update(1, 1, tot, L, R, seg[i].v);
            if (i == (int)seg.size() - 1 || (seg[i].h == seg[i + 1].h && seg[i].v > 0 && seg[i + 1].v < 0) || (seg[i].h != seg[i + 1].h && seg[i].v > 0)) {
                ans = max(ans, tree[1]);
            }
        }
        printf("%lld\n", ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值