题意:
给定
n
n
n条斜率不为0的直线的参数
a
a
a,
b
b
b(
y
=
a
x
+
b
y = ax + b
y=ax+b),有
m
m
m次询问,每次给出一条直线的参数
c
c
c,
d
d
d(
y
=
c
x
+
d
y = cx + d
y=cx+d),该直线与这
n
n
n条直线的交点中,横坐标最大为多少?
思路:
对于两条直线
y
=
a
x
+
b
y = ax + b
y=ax+b 和
y
=
c
x
+
d
y = cx + d
y=cx+d,其交点的横坐标为:
x
=
−
b
−
d
a
−
c
x = - \frac{b - d}{a - c}
x=−a−cb−d
等价为两个点 ( a , b ) (a,b) (a,b)和 ( c , d ) (c,d) (c,d)斜率的相反数。
故该问题可转化为:求出给定一点与平面 n n n个点的斜率最小值。
此时假设我们正在考虑的询问点为
(
c
,
d
)
(c, d)
(c,d),则以直线
x
=
c
x = c
x=c,将平面上的
n
n
n个点分成两部分,而与询问点组成斜率最小的点,在左半区一定在尽量上面的地方,而右半区则尽量在下面的地方,且一定在最外围,即这
n
n
n个点的凸包顶点上。
此时容易发现,我们只需要在左半区维护一个上凸壳,在右半区维护一个下凸壳,对凸壳上的点进行二分即可得到答案。
此题得解。
代码:
#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;
}