版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/89848556
题目链接:https://vjudge.net/problem/Gym-101174B
解题思路:
对于给定的a,b,我们去看最多有多少个点可以大于等于(a,b),最少有多少个点可以大于(a,b),最后就能确定(x0,y0)的排名了。
我们可以把w1看做x,w2看做y,那么就有 (a1-a)*x + (b1-b)*y >= 0,这不就是一条在二维平面直角坐标系上,经过原点斜率为负数的直线吗。而且我们可以确定第一象限肯定大于(a,b),第四象限肯定小于(a,b),在第二象限和第四象限的不能确定,还有在x和y轴上的。
然后将第二象限的点极角排序,第四象限的点极角排序,枚举每个点连接原点做直线,所有点在直线上面的肯定大于(a,b),当枚举第二象限的点时,对于第四象限的点具有单调性,所以可以用总的O(n)的时间筛选出第四象限符合的点,反之也可以。
最后要注意的是在直线上的点要取或者不取,也就是分数相同的情况,都取就会取得最小排名,都不取就取得最大排名。
#include <bits/stdc++.h>
using namespace std;
const int mx = 1e5 + 10;
int n,siz1,siz2;
struct node{
int x,y;
}a[mx],b[mx];
int judge(node p1,node p2,node p0)
{
int ans = (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y);
return ans;
}
bool cmp(node a,node b)
{
int c = judge(node{0,0},b,a);
return c < 0;
}
int main()
{
scanf("%d",&n);
int x,y,x1,y1;
scanf("%d%d",&x,&y);
if(n==1) return 0*puts("1 1");
int l = 0,r = 0,z = 0;
for(int i=1;i<n;i++){
scanf("%d%d",&x1,&y1);
x1 -= x,y1 -= y;
if(x1<0&&y1<0) l++;
else if(x1>0&&y1>0) r++;
else if(x1==0&&y1==0) z++;
else{
if(x1<0||(x1==0&&y1>0)) a[++siz1] = {x1,y1};
else b[++siz2] = {x1,y1};
}
}
sort(a+1,a+siz1+1,cmp);
sort(b+1,b+siz2+1,cmp);
int hi = 0,mi = 1e9;
int L = 1;
for(int i=1;i<=siz1;i++){
while(L<=siz2&&judge(node{0,0},a[i],b[L])>0) L++;
hi = max(hi,i+(siz2-L+1));
while(L<=siz2&&judge(node{0,0},a[i],b[L])==0) L++;
mi = min(mi,i-1+(siz2-L+1));
}
L = 1;
for(int i=1;i<=siz2;i++){
while(L<=siz1&&judge(node{0,0},b[i],a[L])>0) L++;
mi = min(mi,(siz2-i)+L-1);
while(L<=siz1&&judge(node{0,0},b[i],a[L])==0) L++;
hi = max(hi,(siz2-i+1)+L-1);
}
printf("%d %d\n",r+mi+1,hi+z+r+1);
return 0;
}