AcWing 248. 窗内的星星

AcWing 248. 窗内的星星

洛谷题解

一、题目描述

在一个天空中有很多星星(看作平面直角坐标系),已知每颗星星的坐标和亮度(都是整数)。

求用宽为 W、高为 H 的矩形窗口(W,H 为正整数) 能圈住的星星的亮度总和最大是多少。(矩形边界上的星星不算)

输入格式
输入包含多组测试用例。

每个用例的第一行包含 3 个整数:nWH,表示星星的数量,矩形窗口的宽和高。

然后是 n 行,每行有 3 个整数:xyc 表示每个星星的位置 (xy) 和亮度。

没有两颗星星在同一点上。

输出格式
每个测试用例输出一个亮度总和最大值。

每个结果占一行。

数据范围
1n10000,1W,H1000000,0x,y<231

输入样例:

3 5 4
1 2 3
2 3 2
6 3 1

3 5 4
1 2 3
2 3 2
5 3 1

输出样例:

5
6

二、解题思路

考虑把「星星在窗子所在的矩形中」转化为「窗子的右上角在星星上面 H,右边 W 的矩形中」。这样就可以把整个问题转化为找一个点使得覆盖该点的矩形最多。

(如图,矩形框住了两个星星)

现在考虑窗户边框的限制。

如图,虽然窗户的右上角不能落在黑框上,只能落在黑框里,但将黑框的长和宽的两端都减小一个 eps(极小值)得到红框后,窗户的右上角就可以落在红框里的任何一个地方了。

但是如果减小 eps,星星所对应的矩形的坐标就变成了实数。

这时出现了一种方法,将矩形的长宽在两端都减小 0.5,然后把坐标轴向上平移、向右平移 0.5 个单位。最后生成的矩形左下角是 (x,y),右上角是 x+W1,y+H1

这样做为什么是正确的呢?长宽减小的数值需要保证原本有交的矩形仍然有交。由于星星的坐标是整数,所以极限情况(可能会在矩形缩小后没有交的情况)如下图:

在减小 0.5 之后仍然有交(只不过在对 line 进行排序的时候需要把 l 为正的排在前面)。而两端减小 0.5,合起来就是 1,这导致生成的矩形在平移坐标轴之后的坐标可以是整数。

所以这其实是一个很巧妙的方法,很多题解没说清楚。

于是我们可以将每个星星都扩展成一个矩形,这时我们注意到,若两个矩形之间有交集,他们便可以放在同一个窗户中。

如下图:

图中灰色的部分就是两个星星构成的矩形的交集,只要窗户的右上角端点在灰色区域内,就能同时框住两个星星。

此时我们可以将问题转化为:平面上有若干个矩形,每个矩形都带有一个权值,求在哪个坐标上权值的总和最大。

接下来我们就可以使用扫描线来解决这个问题了,若当前星星的亮度值为c ,则矩形的入边的权值设为 1 ,出边为 1 ,此时我们只要求扫描线上的区间最大值即可得出答案,区间查询可以使用 lazy_tag 的方式实现。

代码实现上的一些小细节:

  • 在对 x 坐标进行升序排序时,将 val 值按降序排序,这样才能处理两个矩形贴合的情况。
  • 观察到 0xi,yi231, 所以我们需要将坐标进行离散化处理。

三、问题集

请问一下 样例中第二个答案是6也就是三个星星都能框进去 而数据是这样的:

3 5 4
1 2 3
2 3 2
5 3 1

最左边的点是(1,2)最右边的点是(5,3)
那么想要把这两个同时框进去不是需要至少宽度是6嘛也就是从0框到6
可样例中宽度是5 那么应该会有一个点卡在边界上那么不就不能算是有贡献了嘛?

yxc回复:
0.55.5的框就可以啦,所以宽度是5的话可以同时包含(1,2)(5,3)
它只是要求星星在整点上,但是我框的矩形顶点是不用在整点上的,所以我们可以平移0.5这样就能多框一段了。

四、实现代码

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>

using namespace std;
typedef long long LL;
const int N = 10010;

//在对 x 坐标进行升序排序时,将 k 值按降序排序,这样才能处理两个矩形贴合的情况
struct Seg {
    LL x, y1, y2, c;
    bool operator<(const Seg &t) const {
        if (x == t.x) return c > t.c; // 1在前,-1在后,先处理入边再处理出边
        return x < t.x;
    }
} seg[N << 1];

struct Node {
    int l, r;
    int maxc, add;
} tr[N << 3];

int n, w, h;
vector<LL> ys;

void pushup(int u) {
    tr[u].maxc = max(tr[u << 1].maxc, tr[u << 1 | 1].maxc);
}

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

void pushdown(int u) {
    if (tr[u].add) {
        tr[u << 1].maxc += tr[u].add;
        tr[u << 1].add += tr[u].add;
        tr[u << 1 | 1].maxc += tr[u].add;
        tr[u << 1 | 1].add += tr[u].add;
        tr[u].add = 0;
    }
}

void modify(int u, int l, int r, int c) {
    if (l <= tr[u].l && tr[u].r <= r) {
        tr[u].maxc += c;
        tr[u].add += c;
        return;
    }

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify(u << 1, l, r, c);
    if (r > mid) modify(u << 1 | 1, l, r, c);

    pushup(u);
}

int find(LL x) {
    return lower_bound(ys.begin(), ys.end(), x) - ys.begin();
}

int main() {
    //加快读入
    ios::sync_with_stdio(false), cin.tie(0);

    while (cin >> n >> w >> h) {
        ys.clear();
        for (int i = 1; i <= n; i++) {
            LL x, y;
            int c;
            cin >> x >> y >> c;
            //区别在这里!
            /*
            需配合:
            if (x == t.x) return c > t.c; // 1在前,-1在后,先处理入边再处理出边
            */
            seg[2 * i - 1] = {x, y, y + h - 1, c};
            //此处,x+w-1,注意:多减了一个1~
            seg[2 * i] = {x + w - 1, y, y + h - 1, -c};
            ys.push_back(y), ys.push_back(y + h - 1);
        }

        sort(ys.begin(), ys.end());
        ys.erase(unique(ys.begin(), ys.end()), ys.end());

        sort(seg + 1, seg + 1 + 2 * n);

        build(1, 0, ys.size() - 1);

        int res = 0;
        for (int i = 1; i <= 2 * n; i++) {
            res = max(res, tr[1].maxc);
            modify(1, find(seg[i].y1), find(seg[i].y2), seg[i].c);
        }
        printf("%d\n", res);
    }
    return 0;
}
posted @   糖豆爸爸  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2021-12-19 P5709 【深基2.习6】Apples Prologue / 苹果和虫子
2021-12-19 P1421 小玉买文具
2019-12-19 删除校管理员的多余数据
Live2D
点击右上角即可分享
微信分享提示