链接:戳这里
Beam Cannon
Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description
Recently, the γ galaxies broke out Star Wars. Each planet is warring for resources. In the Star Wars, Planet X is under attack by other planets. Now, a large wave of enemy spaceships is approaching. There is a very large Beam Cannon on the Planet X, and it is very powerful, which can destroy all the spaceships in its attack range in a second. However, it takes a long time to fill the energy of the Beam Cannon after each shot. So, you should make sure each shot can destroy the enemy spaceships as many as possible.
To simplify the problem, the Beam Cannon can shot at any area in the space, and the attack area is rectangular. The rectangle parallels to the coordinate axes and cannot rotate. It can only move horizontally or vertically. The enemy spaceship in the space can be considered as a point projected to the attack plane. If the point is in the rectangular attack area of the Beam Cannon(including border), the spaceship will be destroyed.
Input
Input contains multiple test cases. Each test case contains three integers N(1<=N<=10000, the number of enemy spaceships), W(1<=W<=40000, the width of the Beam Cannon’s attack area), H(1<=H<=40000, the height of the Beam Cannon’s attack area) in the first line, and then N lines follow. Each line contains two integers x,y (-20000<=x,y<=20000, the coordinates of an enemy spaceship).
A test case starting with a negative integer terminates the input and this test case should not to be processed.
Output
Output the maximum number of enemy spaceships the Beam Cannon can destroy in a single shot for each case.
Sample Input
2 3 4
0 1
1 0
3 1 1
-1 0
0 1
1 0
-1
Sample Output
2
2
题意:
二维平面上给出n个点,给定固定的矩形大小,问任意放置矩形,最多可以覆盖多少个点
思路:
1:窗口的位置不固定,也就是在平面上移动
这里讲下扫描线的原理,已知一个矩形,当前位置x对于矩形下边的加入,必定在x+width的位置线段要出去
类似这样的思想,现在我们在滑动窗口找出覆盖最多的点的位置,那么必定对应一些点由于窗口的移动而从窗口出去,同样也会有新的点进来,那么我们怎么处理可以使得点它自己进来或者自己消失呢?
扫描线思想,对于每个平面上的点(x,y,1)都一一对应一个点(x+W,y,-1) (两处的y都未知)表示窗口出去的时候,当前区域内的点的个数-1,点进来的时候,窗口的数目+1
2:那么只需要把点按x关键字从小到大排序,每次加入一个点,判断这个点是出来还是进去的
现在就是怎么快速求出 ,在已知当前点y的坐标的情况下,区域内点的个数?
这样想:由于每个点(x,y)可以影响到的Y轴的范围是:y-H,y+H,那么我们直接把每个点都简化成一条朝上的线段铺在Y轴上,这样的话只需要求出Y轴上被覆盖次数的那个点的覆盖次数就知道答案了,线段树维护就可以了!
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,H,W;
const int N=10005;
struct point{
int x,y,v;
point(int x=0,int y=0,int v=0){}
bool operator < (const point &a)const{
if(x==a.x) return v<a.v;
return x<a.x;
}
}s[2*N];
struct node{
int f,v;
}tr[4000100];
void build(int root,int l,int r){
tr[root].f=0;
tr[root].v=0;
if(l==r) return ;
int mid=(l+r)/2;
build(root*2,l,mid);
build(root*2+1,mid+1,r);
}
void pushdown(int root){
if(tr[root].f){
tr[root*2].f+=tr[root].f;
tr[root*2+1].f+=tr[root].f;
tr[root*2].v+=tr[root].f;
tr[root*2+1].v+=tr[root].f;
tr[root].f=0;
}
}
void update(int root,int l,int r,int x,int y,int v){
if(x<=l && y>=r){
tr[root].f+=v;
tr[root].v+=v;
return ;
}
pushdown(root);
int mid=(l+r)/2;
if(y<=mid) update(root*2,l,mid,x,y,v);
else if(x>mid) update(root*2+1,mid+1,r,x,y,v);
else {
update(root*2,l,mid,x,mid,v);
update(root*2+1,mid+1,r,mid+1,y,v);
}
tr[root].v=max(tr[root*2].v,tr[root*2+1].v);
}
int main(){
while(scanf("%d",&n)!=EOF){
if(n==-1) break;
scanf("%d%d",&W,&H);
for(int i=1;i<=2*n;i+=2){
int x,y;
scanf("%d%d",&x,&y);
y+=20000;
s[i].x=x;
s[i].y=y;
s[i].v=1;
s[i+1].x=x+W+1;
s[i+1].y=y;
s[i+1].v=-1;
}
sort(s+1,s+2*n+1);
build(1,0,8*N);
int ans=0;
for(int i=1;i<=2*n;i++){
update(1,0,8*N,s[i].y,s[i].y+H,s[i].v);
ans=max(ans,tr[1].v);
}
printf("%d\n",ans);
}
return 0;
}