牛客网暑期ACM多校训练营(第二场)C.message (凸包维护+三分)

链接: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条直线的 y = a x + b 中的 a b ,和m次查询,每次查询给要求的直线的 y = c x + d ,中的 c d ,要求出这条直线与n条直线交点中,x最大的值,且x大于0,如果不存在输出No cross

思路

很容易得到每个交点的 x = b d a c 可以看作是两个点 ( a , b ) ( c , d ) 斜率的相反数,那么问题就转换成已有n个点,求新加入的点与n个点斜率中的最小值,那么现在首先要解决的就是怎么求解这个问题
我们对n个点和要求的点一起对x值进行排序,那么我们会得到以要求的点作为分界线的左边和右边两部分,为了方便记要求的点为 O
这里写图片描述
我们只对 O 左边进行操作,连上一些线

这里写图片描述
O A , B , C 中可以得到一个最小的斜率值,对于 O 来说实际上查询的时候 O C 斜率会比 O B 斜率小,因为 C B 的上方,想要快速得到 C B 的关系的时候可以通过构建凸包这个过程优化,然后一边构建凸包一边实现查询,在 O 之前构建的凸包可以继续构建直到遇到下一个要查询的点即可
这里写图片描述
对于 O 来说只需要在现在这个凸包上查询即可
这里写图片描述
我们连接 O 与凸包上面的点我们观察一下斜率
发现斜率是存在先上上后下降的情况的,实际上会出现的就是三种情况
这里写图片描述这里写图片描述
这里写图片描述
那我们其实发现可以用三分来处理这个极值,三分 m i d m i d + 1 ,两个值比较,要是 O m i d 的斜率小于 O m i d + 1 的斜率说明最小值在 l m i d 的范围内反之在 m i d r
上面我们只处理了要查询的 O 的左边的值,而在他右边的值都还没有处理,所以我们可以把x从大到小再排序一次然后再建立凸包进行相同的处理就好了
这里写图片描述
总结一下我们可以把 n + m 点进行排序然后如果不是查询点就将其加到凸包栈中,如果是查询点就进行三分查询,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;
}

猜你喜欢

转载自blog.csdn.net/ftx456789/article/details/81156460