POJ 2482 Stars in Your Window (线段树扫描线)


题意:

给定n个星星的坐标(x,y)以及亮度c ,求用一个宽W,高H的框(不含边界),能框住的星星的亮度总和的最大值为多少。

( 0<= x,y <2^31 , 1<=c<=100    , 1<=W , H <= 1000000    ,   x,y,c,W,H都是整数)

思路:

用矩形右上角坐标(X,Y)来代表矩形位置,原问题等价于,X,Y为整数,用一个宽W,高H的框(不含左,下边界),能框住的星星的亮度总和的最大值为多少。因为,若X,Y为整数,并且四边上都有星星,那么将该方框向右和上分别平移1/2个单位,就可以在不丢失框内星星的情况下框进上边框和右边框的星星。


转换之后,对于星星(x,y),矩形的右上角坐标的范围在 x-[x,x+W), y-[y,y+H) 这个区域内时,算作框进了这颗星星,要算上这颗星星的亮度。


于是,星星变成了带权值的矩形,矩形框变成了点,问题就是在矩形的重合图中,找到权值最大的点。

这就可以用线段树扫描线了,就是,用一条垂直于x轴的线来从左到右扫描整个图形。整个扫描过程中,线上权值最大的一点就是答案。

线上的权值需要进行区间修改以及维护最大值,这里就用线段树来储存。

将Y值离散化(去除重复值),然后,将星星先根据X值,再根据Y值排序。

然后不断将新的矩形加入,旧的矩形删除就行了。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#define  inf  0x1fffffff
#define maxn 20007
#define LL long long
using namespace std;
//SBT 离散化 
LL Rank[maxn],Rn;
void SetRank(){
	sort(Rank+1,Rank+1+Rn); 
	int I=1;
	for(int i=2;i<=Rn;++i) if(Rank[i]!=Rank[i-1]) Rank[++I]=Rank[i];
	Rn=I;
} 
int GetRank(LL x){
	int L=1,R=Rn,M;//[L,R] first >=x
	while(L!=R){
		M=(L+R)>>1;
		if(Rank[M]<x) L=M+1;
		else R=M;
	}
	return L;
}
//储存数据 
struct Star{//记录星星,之后要排序 
	LL x,y;int c;
	Star(){}
	Star(LL x,LL y,int c):x(x),y(y),c(c){}
	bool operator < (const Star &B)const{
		return x < B.x|| x==B.x && y < B.y;
	}
}St[maxn>>1];
int n,W,H;
//线段树 区间加&&最大值 
int Max[maxn<<2],Add[maxn<<2];
void PushUp(int rt){
	Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
}
void PushDown(int rt){
	if(Add[rt]){
		Add[rt<<1]+=Add[rt];Add[rt<<1|1]+=Add[rt];
		Max[rt<<1]+=Add[rt];Max[rt<<1|1]+=Add[rt];
		Add[rt]=0;
	}
}
void Update(int L,int R,int C,int l,int r,int rt){
	if(L <=l && r <= R){Add[rt]+=C;Max[rt]+=C;return;}
	PushDown(rt);
	int m=(l+r)>>1;
	if(L <= m) Update(L,R,C,l,m,rt<<1);
	if(R >  m) Update(L,R,C,m+1,r,rt<<1|1); 
	PushUp(rt);
}
int main(void)
{
	while(~scanf("%d%d%d",&n,&W,&H)){
		memset(Max,0,sizeof(Max));
		memset(Add,0,sizeof(Add));
		Rn=0;
		for(int i=1;i<=n;++i) {
			scanf("%I64d%I64d%d",&St[i].x,&St[i].y,&St[i].c);
			Rank[++Rn]=St[i].y;
			Rank[++Rn]=St[i].y+H;
		}
		SetRank();
		sort(St+1,St+n+1);//给星星排序 
		int L=1,X,ANS=0;
		for(int i=1;i<=n;++i){
			Update(GetRank(St[i].y),GetRank(St[i].y+H)-1,St[i].c,1,Rn,1);//加入新矩形 
			X=St[i].x;//更新当前X 
			while(St[L].x+W <= X){//删除已经对横坐标为X的扫描线无影响的矩形 
				Update(GetRank(St[L].y),GetRank(St[L].y+H)-1,-St[L].c,1,Rn,1);
				++L;
			}
			ANS=max(ANS,Max[1]);//求最大值 
		}
		printf("%d\n",ANS);//输出答案 
	}
return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值