2018暑假牛客多校二 C(凸包)

题目描述:

    有一个无线大的平面,平面上有n条斜率不为零的直线。 有m次询问,每次询问从y轴上的一个点出发往x轴正方向沿一条直线走,最后一个与这n条直线相交的点的 横坐标。 n,m<=50000

题目分析:

    根据题意我们可以,如果一条直线为 y=ax+b,另一条直线为 y=cx+d,则我们容易得出他们的交点的横坐标为 。

因此我们可以将这个横坐标看作是两个点(a,b),(c,d)之间的斜率的相反数。

    因此我们就可以将题目转化成:在平面内又n个点pi,每次给一个点p,问这个点到平面内所有点的斜率最小值。

    而因为最小值相对来说没这么好处理,因此我们则可以将所有点的横坐标取反,则这个题就转化为求斜率的最大值。

    对于这个问题,我们就可以通过凸包进行维护。

    具体的做法是:我们先根据题目给出的n个直线(将他们看成一个个点对),做一个凸包。因为做出来的凸包上的每一个点是存在一定的单调性的,因此我们可以在凸包上二分查找出跟询问的点p斜率最大的点。(具体的二分操作是如果发现当前选到的凸包上的两点p1,p2与询问的点p的叉积,则证明 pp1的斜率小于pp2,则向左区间二分,以此类推)

    当找到斜率最大的点后,不断更新答案即可。

代码:

#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
const double eps=1e-8;
int sgn(double x){
    if(fabs(x)<eps) return 0;
    if(x<eps) return -1;
    else return 1;
}
struct Point{
    double x,y;
    int id;
    Point(double _x=0, double _y=0,int _id=0){
        x=_x,y=_y,id=_id;
    }
    Point operator -(const Point&q){
        return Point(x-q.x,y-q.y);
    }
    double operator *(const Point &q){
        return x*q.x+y*q.y;
    }
    double operator ^(const Point &q){
        return x*q.y-y*q.x;
    }
    bool operator <(const Point &q){
        if(sgn(x-q.x)==0) return y<q.y;
        return x<q.x;
    }
};
double getk(Point a){
    return a.y/a.x;
}
double cross(Point a,Point b,Point c){
    return (b-a)^(c-a);
}
Point p[maxn],q[maxn];
double ans[maxn];
void Graham(int n){
    int num=0;
    for(int i=0;i<n;i++){
        if(!p[i].id){
            while(num>1&&sgn(cross(q[num-2],q[num-1],p[i]))<=0)
            num--;
            q[num++]=p[i];
        }
        else{
            int l=0,r=num-1;
            while(l<r){
                int mid=(l+r)>>1;
                if(sgn(cross(p[i],q[mid],q[mid+1]))<=0) r=mid;
                else l=mid+1;
            }
            if(l<num) ans[p[i].id]=max(ans[p[i].id],getk(p[i]-q[l]));
        }
    }
}
int main()
{
    int n,m;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%lf%lf",&p[i].x,&p[i].y),p[i].x=-p[i].x;
    }
    scanf("%d",&m);
    for(int i=0;i<m;i++){
        scanf("%lf%lf",&p[i+n].x,&p[i+n].y);
        p[i+n].id=i+1,p[i+n].x=-p[i+n].x;
    }
    sort(p,p+n+m);
    Graham(n+m);
    reverse(p,p+n+m);
    Graham(n+m);
    for(int i=1;i<=m;i++){
        if(sgn(ans[i])==0) puts("No cross");
        else printf("%.8f
",ans[i]);
    }
}
原文地址:https://www.cnblogs.com/Chen-Jr/p/11007260.html