题意:
给出n条直线,询问m次,每次询问给出一条直线,问这条直线与那n条直线的在y轴右侧交点的横坐标的最大值。
题解:
设交点为,则
要使x最大,我将一条直线的两个系数a、b看做点(a,b),那么答案就是n个点中到询问点的斜率的最小值。
为了方便和便于理解,我们将x坐标取反,转为求斜率的最大值。
求上图黑点到红点的斜率最大值,可以变为只求下图连线中的点的斜率
那些连线上方的点一定不是最优解
所以我们要做的就是维护一个凸包
询问某个点时,答案就是凸包中与询问点相切的点
这个找相切的点的过程可以用二分查找,原因如下
对于切点左侧的点,相邻两点组成的向量与向量a的叉积大于0,
对于切点右侧的点,相邻两点组成的向量与向量a的叉积小于0,
满足大于0的最右面的点就是答案需要的点
当然,答案也可能是这种情况
所以我们需要反过来再求一次
代码:
#include<bits/stdc++.h>
#define N 100010
#define INF 0x3f3f3f3f
#define eps 1e-10
#define pi 3.141592653589793
#define LL long long
#define pb push_back
#define cl clear
#define si size
#define lb lower_bound
#define mem(x) memset(x,0,sizeof x)
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;
struct Point
{
LL x,y;LL id;
Point (LL x=0,LL y=0):x(x),y(y){}
bool operator < (const Point z)
{
return x==z.x?y<z.y:x<z.x;
}
double K()
{
return (double)y/x;
}
}a[N],b[N];
double ans[N];
typedef Point Vector;
Vector operator + (Vector a,Vector b){return Vector(a.x+b.x,a.y+b.y);}
Vector operator - (Vector a,Vector b){return Vector(a.x-b.x,a.y-b.y);}
LL Cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;} //叉积
void spy(int n)
{
int m = 0;
for(int i = 0; i < n; i++)
if (a[i].id)
{
if (!m) continue;
int l=1,r=m-1,t=0,v=0;
while(l<=r)
{
int t=l+r>>1;
if (Cross(b[t]-b[t-1],a[i]-b[t])>=0)
v=t,l=t+1;else r=t-1;
}
ans[a[i].id]=max(ans[a[i].id],(a[i]-b[v]).K());
}
else
{
while(m > 1 && Cross(b[m-1] - b[m-2], a[i]-b[m-2]) <= 0) m--;
b[m++] = a[i];
}
}
int main()
{
int n,m;
while(~sc(n))
{
for (int i=0;i<n;i++) scanf("%lld%lld",&a[i].x,&a[i].y),a[i].id=0;
sc(m);
for (int i=n;i<n+m;i++) scanf("%lld%lld",&a[i].x,&a[i].y),a[i].id=i-n+1;
for (int i=1;i<=m;i++) ans[i]=-1;
for (int i=0;i<n+m;i++) a[i].x=-a[i].x;
sort(a,a+n+m);
spy(n+m);
reverse(a,a+n+m);
spy(n+m);
for (int i=1;i<=m;i++)
if (ans[i]<0) puts("No cross");else
printf("%.8f\n",ans[i]);
}
}