牛客网暑期ACM多校训练营(第二场)-C-message

(一)题面:

题目描述 :

There is an infinite plane. White Cloud has n lines which are not parallel to the Oy axis. These lines in the plane are in the form y=ax+b.
White Rabbit will have a trip in the plane. It will start at time 0 and go straight along a line. specifically, White Rabbit uses 2 parameters C and D,denoting that at time x, White Rabbit is at the position(x,C*x+D).
If at some time, White Rabbit is located at one of White Cloud's lines, White Cloud will receive a message immediately.

White Rabbit has m pairs (C[i],D[i]) for i=1..m. For each i=1..m, White Cloud wants to know if White Rabbit uses (C[i],D[i]) , when is the last time White Cloud can receive a message.
 

 

输入描述:

The first line of input contains an integer n. (n<=50000)
For the next n lines, the i-th line contains 2 integers A[i], B[i], describing the i-th line. (-1e9<=A[i],B[i]<=1e9)
All numbers A[i] are different.
The next line contains an integer m. (m<=50000)
For the next m lines, the i-th line contains 2 integers C[i],D[i], describing the i-th pair.(-2e9<=C[i],D[i]<=2e9)
Each C[j] is different from any of the numbers A[i].
Each D[j] is different from any of the numbers B[i].

输出描述:

Print m lines. The i-th line contains a real number with at least 6 digits after the decimal point, denoting the latest time White Cloud can receive a message. Your answer must be correct within an absolute error of 1e-6.
If White Cloud can't receive any message during White Rabbit's trip,print a string "No cross".

示例1

输入

2
0 -1
1 2
3
-1 4
2 -2
2 5

输出

5.000000000000000
4.000000000000000
No cross

说明

 

(二)题意:

在平面上有许多斜率不为零的直线,直线y=a*x+b以点对(a,b)的形式给出,现有若干次询问,每一次询问给出点对(c,d)表示直线y=c*x+d,要你求该直线与给出若干条直线的所有的交点中横坐标的最大值。

 

(三)题解:

①首先考虑两条直线的交点,由:y=a*x+b与y=c*x+d可以解出来x=-(b-d)/(a-c);

②如果将(a,b)、(c,d)看作两个点,那么-(b-d)/(a-c),显然就是两点之间连线的斜率的相反数;

③那么问题就转化为了:平面上有若干个点,每次给一个点,问这个点到平面上所有点的斜率最小值;

④然后就是用凸包维护。

⑤以上便是官方的题解,下面具体分析一下如何用凸包进行维护。

PS:凸包及其算法简介

Ⅰ)这里我们用Andrew扫描法求解凸包;

Ⅱ)为了便于理解,这里我们先将所有的点的横坐标取负,这样就成了求斜率的最大值(其实求法是一样的,只是我觉得这样好理解一点);

Ⅲ)假设我们已经求得了给定得点的凸包的一部分,现在需要求解的点为P,如下图:

图中的黑色折现即为当前求得的凸包,我们连接P和各个顶点,可以很直观发现有一个“拐角”上的点Q和P点间的连线的斜率最大,接着我们就是求解Q这个点。

Ⅳ)可以发现对于Q以后的点我们都有向量d叉乘向量c小于(等于)零,而对于Q之前的点有b×a>=0,根据这一性质我们便可以二分地去得到点Q。

Ⅴ)由于Andrew算法一次扫描只会得到半个凸包,故还需要反着进行一次扫描,查找情况与上述一致,然后保留最大值即可。

Ⅵ)复杂度O(nlogn),

 

(四)代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=1e6+10;
const double esp=1e-10;
double ans[maxn];
struct Point{
    int id;
    double x,y;
    Point(){x=y=id=0;}
    Point(double _x,double _y){
        x=_x;y=_y;
    }
}P[maxn],Convexhull[maxn];
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);
}
Vector operator * (Vector A,double d){
    return Vector(A.x*d,A.y*d);
}
Vector operator / (Vector A,double d){
    return Vector(A.x/d,A.y/d);
}
double Dot(Vector A,Vector B){
    return A.x*B.x+A.y*B.y;
}
double Cross(Vector A,Vector B){
    return A.x*B.y-A.y*B.x;
}
double Length(Vector A){
    return sqrt(Dot(A,A));
}
int dcmp(double x){
    return fabs(x)<esp?0:x<0?-1:1;
}
bool operator <(Point p1,Point p2){
    return dcmp(p1.x-p2.x)<0||(dcmp(p1.x-p2.x)==0&&dcmp(p1.y-p2.y)<0);
}
double K(Vector v){
    return v.y/v.x;
}
void Scan(int n){
    int top=0;
    for(int i=0;i<n;i++){
        if(P[i].id){
            if(!top)continue;
            int l=0,r=top-1;
            while(l<r){
                int m=(l+r)>>1;
                if(Cross(Convexhull[m+1]-Convexhull[m],P[i]-Convexhull[m])<=0)r=m;
                else l=m+1;
            }
            ans[P[i].id]=max(ans[P[i].id],K(P[i]-Convexhull[l]));
        }
        else{
            while(top>1&&dcmp(Cross(P[i]-Convexhull[top-2],Convexhull[top-1]-Convexhull[top-2]))>=0)top--;
            Convexhull[top++]=P[i];
        }
    }
}
void Andrew(int n){
    sort(P,P+n);Scan(n);
    reverse(P,P+n);Scan(n);
}
int main(){
    freopen("in.txt","r",stdin);
    int n,m;
    scanf("%d",&n);
    for(int i=0;i<n;i++)scanf("%lf%lf",&P[i].x,&P[i].y);
    scanf("%d",&m);
    for(int i=n;i<n+m;i++)scanf("%lf%lf",&P[i].x,&P[i].y),P[i].id=i-n+1;
    for(int i=0;i<n+m;i++)P[i].x=-P[i].x;
    Andrew(n+m);
    for(int i=1;i<=m;i++){
        if(dcmp(ans[i])<=0)printf("No cross\n");
        else printf("%.12lf\n",ans[i]);
    }
    return 0;
}

 

(五)总结:

看了题解以后发现自己还停留在看懂题解的前一个阶段就很尴尬...

计算几何,重新好好地学习了一下凸包。

在二分求Q的时候开始没有想明白,以为是二分斜率,然而斜率显然不存在单调性的,这里的二分求解有点二分求零点的味道~,但是总感觉有点怪怪的,1A还是有点意外ヾ(≧▽≦*)o。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值