题目大意:今晚有流星划过,求在某瞬间最多有多少流星能被拍进镜头?
分析:咋看之下有点复杂。一点点分析后,可以发现这题是借助扫描线来解决,类似区间并,面积并。
每个流星可以看做一个开区间,这个问题可以转化为,求扫描线在哪个位置时与最多的开区间相交。
首先,我们需要将流星转化为开区间,如何转化呢?题目给的数据有坐标x,y,以及xy方向上的速度,那么我们很自然地想到分解成xy两个方向上来求区间,然后得到的两个区间的相交部分,就是该流星的开区间。
然后,就可以用扫描线啦~
需要注意的是,当某一位置既有前一个区间的右端点,又有后一个区间的左端点时,我们先操作右端点,再操作左端点。很好理解,因为在镜头边缘的点,我们并不计数。
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 111111;
struct Event {
int x;
int type;
bool operator < (const Event & cmp) {
return x < cmp.x || (x == cmp.x && type > cmp.type);
}
}e[2*maxn];
void update(int a, int x, int w, int& L, int& R) {
if(a == 0) {
if(x <= 0|| x >= w) R = L-1;
}
else if(a > 0) {
L = max(L, -x*2520/a);
R = min(R, (w-x)*2520/a);
}
else {
L = max(L, (w-x)*2520/a);
R = min(R, -x*2520/a);
}
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
int n, w, h;
int a, b, x, y;
scanf("%d%d%d", &w, &h, &n);
int cnt = 0;
for(int i = 0; i < n; i++) {
scanf("%d%d%d%d", &x, &y, &a, &b);
int L = 0, R = 1 << 30;
update(a, x, w, L, R);
update(b, y, h, L, R);
if(R > L) {
e[cnt++] = (Event){L, 0};
e[cnt++] = (Event){R, 1};
}
}
sort(e, e+cnt);
int ans = 0;
int tt = 0;
for(int i = 0; i < cnt; i++) {
if(e[i].type == 0) {
tt++;
ans = max(ans, tt);
}
else tt--;
}
printf("%d\n", ans);
}
return 0;
}