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;
}