题意:给你一个左下端点为(0,0),右上端点为(w,h)的矩形照相机,还有n个流星的初始位置与速度,求能照到流星的最多的个数,注意当流星在矩形边界上时视为照不到
每个初始位置和速度都用坐标的形式给出
本题可以抽象成这样一个问题:给出n个开区间(li,ri),你需要求出一个数t,使包含t的区间的个数尽量多
我们可以把每一个t再抽象成一根扫描线,不难发现,当扫描线移动到某个区间的左端点稍稍右移的位置是最可能得到一个最大数量的,为了快速找出这些位置扫描线与区间相交的个数我们要维护一个信息而非重新计算
我们把“扫描线碰到一个左端点”和“扫描线碰到一个右端点”看成两个事件,则每遇到一个“左端点事件”就+1.否则就-1
那么如果一个区间的右端点和另一个区间的左端点重合了怎么办呢?我们只需先处理右端点事件就可以了
我们不妨把它理解成将所有区间的右端点往左移动一个非常小的距离
那么这道题的l就是流星刚刚进入矩形的时刻,r就是流星刚刚离开矩形的时刻,另外的一些细节在代码中体现
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100005,inf=1e9;
struct wk{
double x;
int kind;
bool operator < (const wk&a ) const{
return x<a.x||(x==a.x&&kind>a.kind);
}
}s[maxn<<1];
void update(int x,int a,int w,double& l,double& r){
if(a==0){
if(x<=0||x>=w)r=l-1;
}
else if(a>0){
l=max(l,-(double)x/a);
r=min(r,(double)(w-x)/a);
}
else {
l=max(l,(double)(w-x)/a);
r=min(r,-(double)x/a);
}
}
int main(){
int t,n,w,h;
scanf("%d",&t);
while(t--){
int x,a,b,y,tot=0,i;
scanf("%d%d%d",&w,&h,&n);
for(i=1;i<=n;i++){
scanf("%d%d%d%d",&x,&y,&a,&b);
double l=0,r=inf;
update(x,a,w,l,r);
update(y,b,h,l,r);
if(r>l){
s[++tot]=(wk){l,0};
s[++tot]=(wk){r,1};
}
}
int cnt=0,ans=0;
sort(s+1,s+1+tot);
for(i=1;i<=tot;i++)
if(!s[i].kind)ans=max(ans,++cnt);
else cnt--;
cout<<ans<<endl;
}
}