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;
}

浙公网安备 33010602011771号