给定 n n n个点,求 两两连线 到原点距离 的第 k k k小值.
n ≤ 1 e 5 , 7.5 s n\le 1e5,7.5s n≤1e5,7.5s
二分答案 r r r,数有多少条线段与圆有交.
重要转化:
对于俩个在圆外的点,他们间的直线和圆有交当且仅当 他们和切点的连线无交.
这三种情况覆盖了所有的 A B AB AB之间的相对位置关系,可以发现仅有第三种可以和圆无交.
自此,我们可以把 A 1 , A 2 A_1,A_2 A1,A2的位置表示为相对于O的角度,这样用树状数组求解即可.
int n;
db dis[N], ang[N];
ll k;
const db PI=acos(-1.0),eps=1e-8;
bool check(db r) {//与半径为r的圆相交的线段数是否>=k
ll sum=0;
static db L[N],R[N],val[N]; int tot=0,num=0;
FOR(i,n) if(dis[i] > r) {
db a=acos(r/dis[i]);
L[++tot]=ang[i]-a; R[tot]=ang[i]+a;
if(L[tot] < -PI) L[tot] += 2*PI;
if(R[tot] > PI) R[tot] -= 2*PI;
if(L[tot] > R[tot]) swap(L[tot],R[tot]);
val[++num]=L[tot]; val[++num]=R[tot];
}
if(1LL*(n-tot)*(n-tot-1)/2 >= k) return 1;
if(tot) {
sort(val+1,val+num+1); num=unique(val+1,val+num+1)-(val+1);
static pii id[N];
FOR(i,tot) {
id[i].fi=lower_bound(val+1,val+num+1,L[i])-val;
id[i].se=lower_bound(val+1,val+num+1,R[i])-val;
}
sort(id+1,id+tot+1); BIT<int> c(num);
REP(i,1,tot) {
sum += c.sum(id[i].se);
c.add(id[i].fi,1);
c.add(id[i].se,-1);
}
}
sum=1LL*n*(n-1)/2-sum;
return sum >= k;
}
int main() {
qr(n); qr(k);
FOR(i,n) {
int x,y; qr(x); qr(y);
dis[i]=sqrt(x*x+y*y);
ang[i]=atan2(y,x);
}
db l=0,r=sqrt(2e8),mid;
while(l+eps<r) {
mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
printf("%.10lf\n",l);
return 0;
}