链接:https://www.nowcoder.com/acm/contest/140/C
来源:牛客网
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
说明
题意
给你n条直线的 中的 和 ,和m次查询,每次查询给要求的直线的 ,中的 和 ,要求出这条直线与n条直线交点中,x最大的值,且x大于0,如果不存在输出No cross
思路
很容易得到每个交点的
可以看作是两个点
,
斜率的相反数,那么问题就转换成已有n个点,求新加入的点与n个点斜率中的最小值,那么现在首先要解决的就是怎么求解这个问题
我们对n个点和要求的点一起对x值进行排序,那么我们会得到以要求的点作为分界线的左边和右边两部分,为了方便记要求的点为
我们只对
左边进行操作,连上一些线
与
中可以得到一个最小的斜率值,对于
来说实际上查询的时候
斜率会比
斜率小,因为
在
的上方,想要快速得到
和
的关系的时候可以通过构建凸包这个过程优化,然后一边构建凸包一边实现查询,在
之前构建的凸包可以继续构建直到遇到下一个要查询的点即可
对于
来说只需要在现在这个凸包上查询即可
我们连接
与凸包上面的点我们观察一下斜率
发现斜率是存在先上上后下降的情况的,实际上会出现的就是三种情况
那我们其实发现可以用三分来处理这个极值,三分
和
,两个值比较,要是
和
的斜率小于
与
的斜率说明最小值在
到
的范围内反之在
到
中
上面我们只处理了要查询的
的左边的值,而在他右边的值都还没有处理,所以我们可以把x从大到小再排序一次然后再建立凸包进行相同的处理就好了
总结一下我们可以把
点进行排序然后如果不是查询点就将其加到凸包栈中,如果是查询点就进行三分查询,x坐标从小到大一次,从大到小一次,取两次中斜率的最小值,最后再把答案都乘上一个-1,如果答案小于等于0就是No cross,接着输出就行
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;
const int MAXN=2*50005;
struct Node
{
double x,y;
int id;
double ans;
} p[MAXN],S[MAXN];
int n,m;
bool cmp(Node a,Node b)
{
return a.id<b.id;
}
bool cmp1(Node a,Node b)
{
if(a.x==b.x)
return a.id<b.id;
return a.x<b.x;
}
bool cmp2(Node a,Node b)
{
if(a.x==b.x)
return a.id<b.id;
return a.x>b.x;
}
double Cross(Node a,Node b,Node c)
{
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
double Slope(Node a,Node b)
{
return (a.y-b.y)/(a.x-b.x);
}
void work()
{
int top=0;
for(int i=1; i<=n+m; i++)
{
if(!p[i].id)
{
while(top>1&&Cross(S[top-1],p[i],S[top])<=0)//利用叉乘建立凸包
top--;
S[++top]=p[i];
}
else
{
if(top==0)
continue;
int l=1,r=top;
while(l<r)//每次遇到要查询的点的时候用三分查询
{
int mid=(l+r)>>1;
if(Slope(S[mid],p[i])<Slope(S[mid+1],p[i]))
r=mid;
else
l=mid+1;
}
p[i].ans=min(p[i].ans,Slope(p[i],S[l]));
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
memset(p,0,sizeof(p));
for(int i=1; i<=n; i++)
{
scanf("%lf%lf",&p[i].x,&p[i].y);
p[i].id=0;
}
scanf("%d",&m);
for(int i=1; i<=m; i++)
{
scanf("%lf%lf",&p[n+i].x,&p[n+i].y);
p[n+i].id=i;
}
sort(p+1,p+n+m+1,cmp1);
work();
sort(p+1,p+n+m+1,cmp2);
work();
sort(p+1,p+n+m+1,cmp);
for(int i=n+1; i<=n+m; i++)
{
p[i].ans*=-1;
if(p[i].ans<=0)
printf("No cross\n");
else
printf("%.15lf\n",p[i].ans);
}
}
return 0;
}