计算几何(二分答案or二分搜索)

Description
uncle-lu对计算几何有着浓厚的兴趣。他经常对着平面直角坐标系发呆,思考一些有趣的问 题。今天,他想到了一个十分有意思的题目:

首先,uncle-lu会在x轴正半轴和y轴正半轴分别挑选n个点。随后,他将x轴的点与y轴 的点一一连接,形成n条线段,并保证任意两条线段不相交。uncle-lu确定这种连接方式有且仅有一种。最后,uncle-lu会给出m个询问。对于每个询问,将会给定一个点P(xp​,yp​),问线段OP(O为坐标原点)与n条线段会产生多少个交点?
Input
第1行包含一个正整数n,表示线段的数量;

第2行包含n个正整数,表示uncle-lu在x轴选取的点的横坐标;

第3行包含n个正整数,表示uncle-lu在y轴选取的点的纵坐标;

第4行包含一个正整数m,表示询问数量;

随后m行,每行包含两个正整数xp​和yp​,表示询问中给定的点的横、纵坐标。
Output
共m行,每行包含一个非负整数,表示你对这条询问给出的答案。
Sample Input 1
3
4 5 3
3 5 4
2
1 1
3 3
Sample Output 1
0
3
Hint
3条线段分别为:(3, 0)−(0, 3)、(4, 0)−(0, 4)、(5, 0)−(0, 5)

(0, 0)−(1, 1)不与他们有交点,答案为0

(0, 0)−(3, 3)与三条线段均有交点,答案为3。

数据范围:

对于100%的数据:n,m≤10 ^ 5 ,1≤x,y≤2^31。
Time Limit
1000MS
Memory Limit
256MB

分析:通过画图,不难得知,只有将x、y正半轴上2n个点这样匹配,才可以连出n条不相交的线段。
在这里插入图片描述
数值小的点和数值小的点匹配,才能保证线段不相交。
由高中平面几何知识:对于每条线段(0,yi)-(xi,0),可列 截距式方程 x/xi + y/yi =1,当某个点(xp,yp)带入方程后有xp/xi + yp/yi >=1,说明这个点不在这条线段下方,则线段(0,0)-(xp,yp)与包括这条线段在内的左侧所有线段都各有1个交点,反之则点(xp,yp)在这条线段下方,线段(0,0)-(xp,yp)与这条线段没有交点。
将式x/xi + y/yi =1变换,我们就可以通过计算 xp * yi + yp * xi - xi * yi 的值判断线段(0,0)-(xp,yp)与线段(0,yi)-(xi,0)有没有交点。到这里我们已经能看出这题含有单调性,可以二分。我们 对线段从左往右进行编号 ,对编号进行二分枚举,如果编号i对应的 xp * yi + yp * xi - xi * yi 大于等于0,说明有交点,可以再探索更大的编号;否则编号要换得小一点。
由此可见,我们需要对输入数据进行预处理,将x和y从小到大排序,这样就自然得到线段从左往右的序列。另外,在做这题时,看题目数据范围,所有输入数据都可以不开long long,但是计算 xp * yi + yp * xi - xi * yi 时必须转换为long long防止溢出。

参考代码:

#include<stdio.h>
#include<algorithm>

int n,m;//线段数,询问数
int x[100001],y[100001];//线段的x坐标,y坐标
int X,Y;//查询坐标
//计算叉积,判断相交
long long int count(int t)
{
    
    //由数据范围,必须要转为long long,防止进行乘法或加分时溢出
    return (long long)X*y[t]+(long long)Y*x[t]-(long long)x[t]*y[t];
}
//计算交点数
int judge()
{
    
    //如果一个交点也没有,ans不会再被赋值,出循环时仍为0
 //左开右开区间写法,解区间为[1,n]
    int left=0,right=n+1,ans=0,mid;
    long long int temp;
    while(left+1!=right)
    {
    
    //既然要找最大的线段编号,不妨向上取整
        mid=left+((right-left+1)>>1);
        temp=count(mid);
        if(temp>=0){
    
    //点不在编号mid线段下方
            ans=mid;//从线段1到线段mid都与所问线段线段相交
            left=mid;//看看更大的编号会不会相交
        }//点在编号mid线段下方,说明编号更小才有可能相交
        else right=mid;
    }
    return ans;
}

int main()
{
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%d",x+i);
    }
    for(int i=1;i<=n;i++)
    {
    
    
        scanf("%d",y+i);
    }
    scanf("%d",&m);
    std::sort(x+1,x+n+1);//数据预处理
    std::sort(y+1,y+n+1);
    while(m)
    {
    
    
        scanf("%d%d",&X,&Y);
        printf("%d\n",judge());
        --m;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_44643644/article/details/105951201
今日推荐