Live Archive 3905 Meteor

题意:给你一个左下端点为(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;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值