2018 牛客多校第二场 C message

题意:

给出n条直线,询问m次,每次询问给出一条直线,问这条直线与那n条直线的在y轴右侧交点的横坐标的最大值。

题解:

设交点为(x,y),则

ax+b=a_ix+b_i

x=-\frac{b_i-b}{a_i-a}

要使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]);
    }
}

猜你喜欢

转载自blog.csdn.net/nudt_spy/article/details/82766287