题干:
在一个天空中有很多星星(看作平面直角坐标系),已知每颗星星的坐标和亮度(都是整数)求用宽为w、高为h的矩形(w,h为整数)能圈住的星星的亮度总和最大是多少(矩形边界上的星星不算)。
Input
There are several test cases in the input. The first line of each case contains 3 integers: n, W, H, indicating the number of stars, the horizontal length and the vertical height of the rectangle-shaped window. Then n lines follow, with 3 integers each: x, y, c, telling the location (x, y) and the brightness of each star. No two stars are on the same point.
There are at least 1 and at most 10000 stars in the sky. 1<=W,H<=1000000, 0<=x,y<2^31.
Output
For each test case, output the maximum brightness in a single line.
Sample Input
3 5 4
1 2 3
2 3 2
6 3 1
3 5 4
1 2 3
2 3 2
5 3 1
Sample Output
5
6
题解:
因为矩形的大小固定,所以矩形可以由它的任意一个顶点唯一确定它的位置。我们可以考虑把矩形的右上角顶点放到什么位置,圈住的星星亮度总和最大。
对于一个星星(x,y,c),其中x,y为坐标,c为亮度,“能圈住这颗星星的矩形右上角顶点的范围”如下图所示。该范围也是一个矩形,左下角顶点为(x,y),右上角顶点为(x+w,y+h)。为了避免歧义,在接下来的讨论中,我们用“区域”一词来代替这个范围。
问题转化为,**平面上有若干个区域,每个区域都带有一个权值,求在哪个坐标上重叠的区域权值和最大。**其中,每一个区域都是由一颗星星产生的,权值等于星星的亮度,把原问题中的矩形的右上角放在该区域中,就能圈住这几颗星星。
在问题转化后,我们使用扫描线算法,取出每个区域的左右边界,保存成2个四元组(x,y,y+h-1,c)和(x+w,y,y+h-1,-c)。把这些四元组按照横坐标(第一维的值)排序。
同时关于纵坐标值建立一个线段树,维护区间最大值,起初全为0。我们可以认为线段树上的一个“y”代表元区间[y,y+1],而区间[y,y+h-1]可以表示为线段树中的y,y+1,y+2,…,y+h-1这几个值。这样一来,线段树维护的就是若干个数值构成的序列了。
逐一扫描每个四元组(x,y1,y2,c),在线段树中执行区间修改(可用延迟标记实现),把[y1,y2]中每个数值都加c,然后用根节点的dat值更新答案即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int u=20010;
struct rec{unsigned int x,l,r; int c;}a[u];
struct{int p,l,r,dat,add;}t[u*4];
unsigned int b[u],c[u],x,y,w,h;
int n,m,p,i,ans,z;
bool cmp(rec a,rec b)
{
return a.x<b.x||a.x==b.x&&a.c<b.c;
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r,t[p].dat=t[p].add=0;
if(l==r) return;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void spread(int p)
{
if(t[p].add)
{
t[p*2].dat+=t[p].add,t[p*2].add+=t[p].add;
t[p*2+1].dat+=t[p].add,t[p*2+1].add+=t[p].add;
t[p].add=0;
}
}
void change(int p,int l,int r,int c)
{
if(l<=t[p].l&&r>=t[p].r)
{
t[p].dat+=c,t[p].add+=c;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)>>1;
if(l<=mid) change(p*2,l,r,c);
if(r>mid) change(p*2+1,l,r,c);
t[p].dat=max(t[p*2].dat,t[p*2+1].dat);
}
int main()
{
while(cin>>n>>w>>h)
{
for(m=p=0,i=1;i<=n;i++)
{
scanf("%u%u%d",&x,&y,&z);
a[i].l=a[n+i].l=y,a[i].r=a[n+i].r=y+h-1;
a[i].x=x,a[n+i].x=x+w,a[i].c=z,a[n+i].c=-z;
b[++m]=y,b[++m]=y+h-1;
}
sort(b+1,b+m+1);
for(i=1;i<=m;i++)
if(i==1||b[i]!=b[i-1]) c[++p]=b[i];
for(n*=2,i=1;i<=n;i++)
{
a[i].l=lower_bound(c+1,c+p+1,a[i].l)-c;
a[i].r=lower_bound(c+1,c+p+1,a[i].r)-c;
}
sort(a+1,a+n+1,cmp);
build(1,1,p);
for(ans=0,i=1;i<=n;i++)
{
change(1,a[i].l,a[i].r,a[i].c);
ans=max(ans,t[1].dat);
}
cout<<ans<<endl;
}
return 0;
}