题目大意:有一个人在x轴上射箭,他能移动的范围是[0,L],现在有m个靶子,给出每个靶子的坐标(y坐标和两个x坐标),问这个人能否一箭射到所有的靶子
解题思路:二分枚举这个人所在的位置,计算一下当前位置和所有靶子的角度,维护一个角度区间,表示这个区间内的所有靶子都能射中。如果当前位置和一个靶子的角度(两个角度之间的最小值)大于当前区间的最大值,那么这个人就要向右移动,反之,如果小于当前区间的最小值,那么这个人就要向左移动
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define maxn 5010
#define esp 1e-6
struct Target{
double y, x_l, x_r;
}t[maxn];
bool cmp(Target &a, Target &b) {
return a.y < b.y;
}
double w;
int n;
int judge(double pos) {
double L = atan2(t[0].y, t[0].x_r - pos);
double R = atan2(t[0].y, t[0].x_l - pos);
for(int i = 1; i < n; i++) {
double l = atan2(t[i].y, t[i].x_r - pos);
double r = atan2(t[i].y, t[i].x_l - pos);
if(r - L < -esp)
return -1;
if(l - R > esp)
return 1;
L = max(L, l);
R = min(R, r);
}
return 0;
}
bool solve() {
sort(t,t + n, cmp);
double l = 0, r = w;
int mark;
while(r - l > esp) {
double mid = (r + l) / 2;
mark = judge(mid);
if(mark == 0)
return true;
else if(mark == -1)
r = mid;
else
l = mid;
}
return false;
}
int main() {
int test;
scanf("%d", &test);
while(test--) {
scanf("%lf%d", &w, &n);
for(int i = 0; i < n; i++)
scanf("%lf%lf%lf", &t[i].y, &t[i].x_l, &t[i].x_r);
printf("%s\n", solve()?"YES":"NO");
}
return 0;
}