#zkw线段树,扫描线#poj 2482 Stars in Your Window

题目

在第一象限,每颗星星都有亮度,用一个固定长宽的矩形套住星星,使星星的总亮度最大,并且星星不能在矩形的边界上


分析

这道题可以发现如果矩形的左下角是 ( x , y ) (x,y) (x,y),那么右上角就是 ( x + r , y + c ) (x+r,y+c) (x+r,y+c),那么其实可以用扫描线维护横坐标,纵坐标通过线段树得出,然而我比较懒,所以就用zkw线段树,标记永久化


代码

#include <cstdio>
#include <cstring>
#include <algorithm>
typedef unsigned uit;
struct rec{uit x,y1,y2; int w;}a[20001];
uit n,r,c,ty[20001]; int w[50001];
uit in(){
	uit ans=0; char c=getchar();
	while (c<48||c>57) c=getchar();
	while (c>47&&c<58) ans=ans*10+c-48,c=getchar();
	return ans;
}
void print(uit ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
bool cmp(const rec &a,const rec &b){if (a.x!=b.x) return a.x<b.x; else return a.w<b.w;}
int max(int a,int b){return (a>b)?a:b;}
void change(uit k){
	int t=max(w[k<<1],w[k<<1|1]);
	w[k]+=t; w[k<<1]-=t; w[k<<1|1]-=t;
}
int main(){
	while (1){
		if (scanf("%d",&n)!=1) break; r=in(); c=in();
		memset(w,0,sizeof(w));
		for (register uit i=1;i<=n;i++){
			uit x=in(); uit y=in(); int w=in();
			a[i-1<<1|1].x=x; a[i-1<<1|1].y1=y; a[i-1<<1|1].y2=y+c; a[i-1<<1|1].w=w; ty[i-1<<1|1]=y;
			a[i<<1].x=x+r; a[i<<1].y1=y; a[i<<1].y2=y+c; a[i<<1].w=-w; ty[i<<1]=y+c;
		}
		std::stable_sort(ty+1,ty+1+(n<<1));
		std::stable_sort(a+1,a+1+(n<<1),cmp);
		uit m=std::unique(ty+1,ty+1+(n<<1))-(ty+1);
		uit ans=0; uit end=1; while ((end<<=1)<m+2);
		for (register uit i=1;i<=n<<1;i++){
			uit x=std::lower_bound(ty+1,ty+1+m,a[i].y1)-ty;//离散后二分
			uit y=std::lower_bound(ty+1,ty+1+m,a[i].y2)-ty-1;
			for (x+=end-1,y+=end+1;x^y^1;x>>=1,y>>=1){//瞬间跳到叶子节点
				if (!(x&1)) w[x+1]+=a[i].w;//从闭区间到开区间
				if (y&1) w[y-1]+=a[i].w;
				change(x>>1); change(y>>1);
			}
			for (x>>=1;x;x>>=1) change(x);
			ans=max(ans,w[1]);
		}
		print(ans); putchar('\n');
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值