窗口的星星 洛谷P1502 线段树+扫描线+离散化

题目背景

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

题目描述

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

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

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

输入格式

本题有多组数据,第一行为 TT,表示有 TT 组数据。

对于每组数据:

第一行 33 个整数 n,W,Hn,W,H 表示有 nn 颗星星,窗口宽为 WW,高为 HH。

接下来 nn 行,每行三个整数 x_i,y_i,l_ixi​,yi​,li​ 表示星星的坐标在 (x_i,y_i)(xi​,yi​),亮度为 l_ili​。

输出格式

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

输入输出样例

输入 #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

说明/提示

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

数据范围

1\le T \le 101≤T≤10
1\le n \le 10^41≤n≤104
1\le W,H \le 10^61≤W,H≤106 0\le x_i,y_i < 2^{31}0≤xi​,yi​<231

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 20010;
struct Segment_Tree{
	struct info{ //线段树数组
		int l,r;
		ll mx,plz; //mx表示最大值 plz为懒标记
	}node[MAXN << 2];
	void Build(int n,int l,int r) // 先建空树 以y为轴
	{
		node[n].l = l,node[n].r = r;
		if(node[n].l == node[n].r){
			return;
		}
		int mid = (l+r) >> 1,lt = n << 1,rt = n << 1|1;
		Build(lt,l,mid);Build(rt,mid+1,r);
		return;
	}
	void push_down(int n) //懒标记
	{
		if(node[n].plz){
			int lt = n << 1,rt = n << 1|1;
			node[lt].mx += node[n].plz;
			node[rt].mx += node[n].plz;
			node[lt].plz += node[n].plz;
			node[rt].plz += node[n].plz;
			node[n].plz = 0;
		}
		return;
	}
	void modify(int n,int l,int r,int k) //区间修改
	{
		if(node[n].l >= l && node[n].r <= r){
			node[n].plz += k;
			node[n].mx += k;
			return;
		}
		push_down(n);
		int lt = n << 1,rt = n << 1|1;
		if(node[lt].r >= l) modify(lt,l,r,k);
		if(node[rt].l <= r) modify(rt,l,r,k);
		node[n].mx = max(node[lt].mx,node[rt].mx); //维护最大值
		return;
	}
}ST;
int num[MAXN];
struct in{
	int l,r,h,v;  //对h的值按小到大排序 h相同时按v的正负排 因为对两边重合时要先入边再出边
	bool operator < (const in &a) const{ return h == a.h ? v > a.v : h < a.h;} 
}start[MAXN];
void lsh(int n) // 离散化操作
{
	sort(start+1,start+n+1);
	sort(num+1,num+n+1);
	int cnt = unique(num+1,num+n+1) - num - 1;
	for(int i = 1; i <= n; i++){
		int pos1 = lower_bound(num+1,num+cnt+1,start[i].l) - num;
		int pos2 = lower_bound(num+1,num+cnt+1,start[i].r) - num;
		start[i].l = pos1;
		start[i].r = pos2;
	}
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--){
		
		int x,y,l,n,w,h;
		scanf("%d %d %d",&n,&w,&h);
		for(int i = 1;i <= n; i++){
			scanf("%d %d %d",&x,&y,&l);
			num[(i << 1) - 1] = y;
			num[i << 1] = y+h-1;
			start[(i << 1) - 1] = (in) {y,y+h-1,x,l};
			start[i << 1] = (in) {y,y+h-1,x+w-1,-l};
		}
		n <<= 1; // 每个点有两条边
		lsh(n);
		ST.Build(1,1,n);
		ll ans = 0;
		for(int i = 1;i <= n; i++){
			ST.modify(1,start[i].l,start[i].r,start[i].v); //从小到大扫描
			ans = max(ans,ST.node[1].mx); // 记录最大值
		}
		printf("%lld\n",ans);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值