题目:Beam Cannon
题意:给定一个二维平面上的N个点的坐标,要用一个w*h的矩形,问这个矩形最多能罩住多少个点,在边界上也算。
用线段树来做。
对于一个点(x,y),一个矩形能够罩住它的话,首先矩形的右边界必须在x和x+w之间。反过来,这个点能够影响到的范围也就是[x, x+w]。我们可以在添加这个点的时候将[x, x+w]这个区间全部加上1。那么这样最多能覆盖多少个点就变成线段树求最大值的问题了。
这里为了方便计算,先把点全部向右平移20001,x坐标的范围就从[-20000,20000]变成[1, 40001]
再来考虑高度的问题,矩形内的点的高度差不能超过h。
我们可以将点按照y的从小到大排序。然后遍历点,对于当前遍历到的点,我们需要将跟它高度差超过h的删除。我们可以用一个队列来处理,每次把点的信息添加到线段树中时,就顺便把点添加到队列中。每次把队列里面跟当前点高度差大于h的点取出,然后从线段树中把它删除。
每次添加一个点就维护一次答案。当所有点添加过之后,最优答案自然也就出来了。
具体细节看代码吧。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int base = 20001;
const int N = 10000;
const int M = 40001;
#define lson o<<1
#define rson (o<<1)|1
int n, w, h;
struct Point{
int x, y;
bool operator < (const Point &A)const{
return y<A.y || (y==A.y&&x<A.x);
}
}p[N];
int s[M<<2], l[M<<2], r[M<<2], f[M<<2];
void build(int o, int ll, int rr){
s[o]=f[o]=0;
l[o]=ll; r[o]=rr;
if(ll<rr){
int m = (ll+rr)>>1;
build(lson, ll, m);
build(rson, m+1, rr);
}
}
void maintain(int o){
s[o] = max(s[lson], s[rson]);
}
void update(int o, int ll, int rr, int v);
void pushdown(int o){
if(f[o]){
int m = (l[o]+r[o])>>1;
update(lson, l[o], m, f[o]);
update(rson, m+1, r[o], f[o]);
f[o] = 0;
}
}
void update(int o, int ll, int rr, int v){
if(l[o]==ll && r[o]==rr){
s[o] += v;
f[o] += v;
return;
}
pushdown(o);
int m = (l[o]+r[o])>>1;
if(rr<=m) update(lson, ll, rr, v);
else if(ll>m) update(rson, ll, rr, v);
else{
update(lson, ll, m, v);
update(rson, m+1, rr, v);
}
maintain(o);
}
int main(){
while(~scanf("%d", &n) && n>=0){
scanf("%d %d", &w, &h);
for(int i=0; i<n; i++){
scanf("%d %d", &p[i].x, &p[i].y);
p[i].x += base;
}
sort(p, p+n);
int ans = 0;
build(1, 1, M);
queue<Point> Q;
for(int i=0; i<n; i++){
while(!Q.empty() && p[i].y-Q.front().y > h){
int l = Q.front().x;
int r = min(l+w, M-1);
update(1, l, r, -1);
Q.pop();
}
int l = p[i].x;
int r = min(l+w, M-1);
update(1, l, r, 1);
ans = max(ans, s[1]);
Q.push(p[i]);
}
printf("%d\n", ans);
}
return 0;
}