計算幾何(二分答案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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章