题意:
给定
条斜率不为0的直线的参数
,
(
),有
次询问,每次给出一条直线的参数
,
(
),该直线与这
条直线的交点中,横坐标最大为多少?
思路:
对于两条直线
和
,其交点的横坐标为:
等价为两个点 和 斜率的相反数。
故该问题可转化为:求出给定一点与平面 个点的斜率最小值。
此时假设我们正在考虑的询问点为
,则以直线
,将平面上的
个点分成两部分,而与询问点组成斜率最小的点,在左半区一定在尽量上面的地方,而右半区则尽量在下面的地方,且一定在最外围,即这
个点的凸包顶点上。
此时容易发现,我们只需要在左半区维护一个上凸壳,在右半区维护一个下凸壳,对凸壳上的点进行二分即可得到答案。
此题得解。
代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;
const double eps = 1e-8;
const int A = 5e4 + 10;
class P{
public:
ll x,y;
int id;
P(ll _x = 0, ll _y = 0, int _id = 0) {
x = _x; y = _y; id = _id;
}
bool operator < (const P & rhs) const {
return x < rhs.x;
}
P operator - (const P& rhs) const {
return P(x - rhs.x, y - rhs.y);
}
ll operator * (const P& rhs) const {
return x * rhs.y - y * rhs.x;
}
ld operator / (const P& rhs) const {
if(x == rhs.x) return 0.0;
return ld(y - rhs.y) / (x - rhs.x);
}
}a[A], b[A], Sta[A];
int n, m, tot, j;
ld Ans[A];
ld Bi_search(P &x){
int l = 1, r = tot;
while (l < r) {
int mid = (l+r)>>1;
if (Sta[mid]/x > Sta[mid+1]/x) l = mid + 1;
else r = mid;
}
return Sta[l] / x;
}
void solve(){
j = 1;tot = 0;
for (int i = 1; i <= m; i++) {
while (j <= n && a[j].x < b[i].x) {
while (tot >= 2 && (Sta[tot] - Sta[tot-1]) * (a[j] - Sta[tot]) >= 0) tot--;
Sta[++tot] = a[j];
j++;
}
if(tot == 0) Ans[b[i].id] = 1;
else Ans[b[i].id] = Bi_search(b[i]);
}
j = n;tot = 0;
for (int i = m; i >= 1; i--) {
while (j && a[j].x > b[i].x) {
while (tot >= 2 && (Sta[tot] - Sta[tot-1]) * (a[j] - Sta[tot]) >= 0) tot--;
Sta[++tot] = a[j];
j--;
}
if(tot) Ans[b[i].id] = min(Ans[b[i].id], Bi_search(b[i]));
}
}
int main(){
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &a[i].x, &a[i].y);
a[i].id = 0;
}
scanf("%d", &m);
for (int i = 1; i <= m; i++) {
scanf("%lld%lld", &b[i].x, &b[i].y);
b[i].id = i;
}
sort(a + 1, a + 1 + n);
sort(b + 1, b + 1 + m);
solve();
for (int i = 1; i <= m; i++) {
if(Ans[i] <= 0) printf("%.15f\n",-double(Ans[i]));
else printf("No cross\n");
}
return 0;
}