P1502 窗口的星星

P1502 窗口的星星

题目背景

小卡买到了一套新房子,他十分的高兴,在房间里转来转去。

题目描述

晚上,小卡从阳台望出去,“哇~~~~好多星星啊”,但他还没给其他房间设一个窗户。

天真的小卡总是希望能够在晚上能看到最多最亮的星星,但是窗子的大小是固定的,边也必须和地面平行。

这时小卡使用了超能力(透视术)知道了墙后面每个星星的位置和亮度,但是小卡发动超能力后就很疲劳,只好拜托你告诉他最多能够有总和多亮的星星能出现在窗口上。

输入格式

本题有多组数据,第一行为 \(T\),表示有 \(T\) 组数据。

对于每组数据:

第一行 \(3\) 个整数 \(n,W,H\) 表示有 \(n\) 颗星星,窗口宽为 \(W\),高为 \(H\)

接下来 \(n\) 行,每行三个整数 \(x_i,y_i,l_i\) 表示星星的坐标在 \((x_i,y_i)\),亮度为 \(l_i\)

输出格式

\(T\) 个整数,表示每组数据中窗口星星亮度总和的最大值。

输入输出样例 #1

输入 #1

2

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

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

输出 #1

5
6

说明/提示

为了便于理解,输入样例中每组数据之间添加了空行,实际测试数据中并无空行。

小卡买的窗户框是金属做的,所以在边框上的不算在内。

数据范围

对于 \(100\%\) 的数据:\(1\le T \le 10\)\(1\le n \le 10^4\)\(1\le W,H \le 10^6\)\(0\le l_i\le 1000\)\(0\le x_i,y_i < 2^{31}\)

题解

第一眼感觉就是扫描线,嗯,然后不知道怎么去扫描线,ending

话又说回来,不难知道矩形的右上角可以确定唯一的矩形,那对于坐标在 \((x_i,y_i)\) 的星星,能覆盖其的矩形右上角端点的坐标范围 \((x,y)\) 就是 $$x\in[x_i,x_i + w - 1],y\in[y_i,y_i + h - 1]$$
这是一个矩形区域
然后我们将每个星星转化成上述的一个矩形区域,那么问题转化为:
平面上有 \(n\) 个矩形,每个矩形有一个权值,寻找一个点最大化覆盖这个点的矩形的权值和

转化完了,然后我还是不会扫描线

首先对 \(x\) 排序,并在 \(y\) 方向上做离散化,经典扫描线预备操作了
然后对于转化后的每个矩形,两条竖直边,\(x_i\) 较小的是入边(权值为 \(l_i\) ),较大的是出边权值为\(-l_i\) ,每次遇到一条边的时候,我们在这个数值边对应的 \(y\) 方向的所有点都加上该边的权值,那么答案就是每次更新时候 \(y\) 方向上权值总和最大的那个点的权值
也是个经典的扫描线操作了,就是需要去用懒标记完成区间加法并维护区间最大值

代码

#include<bits/stdc++.h> 
// #define mod 998244353
#define int long long   //由于作者比较懒,所以直接使用这个了
using namespace std;
typedef long long ll;
typedef __int128 ix;
typedef long double ldb;
const ll V = 2e5 + 100;
const int INF = 1e9 + 7;
struct segment
{
    int l,r; //竖直方向上这条线段的端点
    int x; //这条线段的 x 坐标
    int val; //要加上/减去的值

    bool operator <(segment other)
    {
        if(x == other.x) return val > other.val;
        return x < other.x;
    }
};
struct node
{
    int l,r; //竖直方向上,离散之后的区间端点(下标)
    ll mx; //需要维护的最大值
    ll lazy; //懒标记
};
void solve() 
{
    int n,w,h;
    cin >> n >> w >> h;
    vector<segment> seg;
    vector<int> line;
    for(int i = 1;i <= n;++i)
    {
        int x,y,l;
        cin >> x >> y >> l;
        line.push_back(y),line.push_back(y + h - 1); //转化后这个星星对应的矩形 y 轴方向的线段
        seg.push_back({y,y + h - 1,x,l}); //对应的矩形左边竖直线,“加入”,权值(亮度)为 l
        seg.push_back({y,y + h - 1,x + w - 1,-l}); 
        //对应矩形右端竖直线,此时相当于这个矩形即将消失,等价于删除,所以权值(亮度)为 -l
    }

    sort(seg.begin(),seg.end());
    sort(line.begin(),line.end());
    line.erase(unique(line.begin(),line.end()),line.end()); //离散化

    int m = line.size(); //line 数组是用来做二分的
    n = seg.size(); //其实相当于 n *= 2,因为每个矩形有两条数值边

    for(int i = 0;i < n;++i) //离散化
    {
        int l = lower_bound(line.begin(),line.end(),seg[i].l) - line.begin();
        int r = lower_bound(line.begin(),line.end(),seg[i].r) - line.begin();
        seg[i].l = l,seg[i].r = r;
    }

    vector<node> tree(4 * m + 1);

    auto build = [&](auto&& self,int now,int l,int r) ->void
    {
        tree[now].l = l,tree[now].r = r;
        tree[now].mx = tree[now].lazy = 0;
        if(l == r) return ;

        int mid = (l + r) >> 1;
        self(self,2 * now,l,mid);
        self(self,2 * now + 1,mid + 1,r);
    };

    auto update = [&](int now) ->void
    {
        tree[now].mx = max(tree[2 * now].mx,tree[2 * now + 1].mx);
    };

    auto pushdown = [&](int now) ->void
    {
        int lc = 2 * now,rc = 2 * now + 1;

        tree[lc].mx += tree[now].lazy;
        tree[rc].mx += tree[now].lazy;

        tree[lc].lazy += tree[now].lazy;
        tree[rc].lazy += tree[now].lazy;

        tree[now].lazy = 0;
    };

    auto modify = [&](auto&& self,int now,int l,int r,ll val) ->void
    {
        int lc = 2 * now,rc = 2 * now + 1;

        if(l <= tree[now].l && tree[now].r <= r) //完全覆盖整体加
        {
            tree[now].mx += val;
            tree[now].lazy += val;
            return ;
        }

        pushdown(now); //否则下传懒标记
        // int mid = (l + r) >> 1;
        if(l <= tree[lc].r) self(self,lc,l,r,val);
        if(tree[rc].l <= r) self(self,rc,l,r,val);
        update(now);
    };

    ll ans = 0;
    build(build,1,1,m);
    for(int i = 0;i < seg.size();++i)
    {
        modify(modify,1,seg[i].l,seg[i].r,seg[i].val);
        ans = max(ans,tree[1].mx);
    }
    cout << ans << "\n";
}
signed main()
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
    int t = 1;
    cin >> t;
    while(t--) solve();
    return 0;
}
posted @ 2026-01-05 16:52  SSL_DFT  阅读(3)  评论(0)    收藏  举报