题目大意
给出n个点,求n(n-1)/2条直线据原点距离中第k小的
n<=1e5,k<=n(n-1)/2
题解
结论题
首先二分答案,变成判断一条直线AB是否与圆有交
结论:直线AB与圆有交,当且仅当A的切点连线A1A2和B的切点连线B1B2不相交
证明感受一下
所以直接离散化+树状数组即可
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define M_PI 3.14159265358979323846
#define abs(x) ((x)>0?(x):-(x))
#define min(a,b) (a<b?a:b)
#define max(a,b) (a>b?a:b)
#define low(x) ((x)&-(x))
#define E 0.000000001
#define ll long long
//#define file
using namespace std;
struct type{double x;int id;} b[200001];
struct Type{int x,y;} c[100001];
double X[100001],Y[100001],dis[100001],S[100001],L,R,Mid;
int tr[200001],n,i,j,k,l;
ll K;
void change(int t,int s) {while (t<=n*2) tr[t]+=s,t+=low(t);}
int find(int t) {int ans=0; while (t) ans+=tr[t],t-=low(t);return ans;}
bool cmp(type a,type b) {return a.x<b.x;}
bool Cmp(Type a,Type b) {return a.x<b.x;}
bool pd(double r)
{
double s1,s2,ss,s;
ll sum=0;
l=0;
fo(i,1,n)
if (dis[i]>r)
{
s=acos(r/dis[i]);
s1=S[i]-s,s2=S[i]+s;
if (s1<0) s1+=M_PI*2;
if (s2>M_PI*2) s2-=M_PI*2;
if (s1>s2) ss=s1,s1=s2,s2=ss;
b[++l]={s1,-i},b[++l]={s2,i};
}
if (l)
{
fo(i,1,n) c[i]={1,0};
sort(b+1,b+l+1,cmp);
fo(i,1,l)
{
if (b[i].id<0) c[-b[i].id].x=i;
else c[b[i].id].y=i;
}
l=0;
fo(i,1,n) if (c[i].x<=c[i].y) c[++l]=c[i];
sort(c+1,c+l+1,Cmp);
memset(tr,0,sizeof(tr));
fd(i,l,1)
{
sum+=find(c[i].y);
change(c[i].x,1),change(c[i].y,-1);
}
}
sum=1ll*n*(n-1)/2-sum;
return sum>=K;
}
int main()
{
#ifdef file
freopen("CF1446F.in","r",stdin);
#endif
scanf("%d%lld",&n,&K);
fo(i,1,n)
{
scanf("%lf%lf",&X[i],&Y[i]),dis[i]=sqrt(X[i]*X[i]+Y[i]*Y[i]),S[i]=atan2(Y[i],X[i]);
if (S[i]<0) S[i]+=M_PI*2;
}
L=0,R=20000;
while (R-L>E)
{
Mid=(L+R)/2;
if (!pd(Mid))
L=Mid; else R=Mid;
}
printf("%.15lf
",L);
fclose(stdin);
fclose(stdout);
return 0;
}