Link
记(O(x,y))。
我们将这道题分为两个部分:首先求出第(m)远的交点到(O)的距离,然后再计算前(m)远的交点到(O)的距离之和。
一、求第(m)远的交点
先二分答案(r),接下来就变成了判定是否有(m)个交点在(igcirc(O,r))内。
注意到在(igcirc)内的交点一定满足生成该交点的两条直线都与(igcirc)相交或相切。
将所有直线与(igcirc)的交点(相切的算两个)按与(O)点连线的极角排序。
可以发现两条直线的交点在(igcirc)内的充要条件是两条直线与(igcirc)的交点交错分布。
那么用BIT简单维护即可。
二、求前(m)远的交点到(O)的距离之和
注意到(mle10000000),因此使用链表维护交点,然后(O(m))暴力找出每一个交点即可。
Tips:
可能会有一些点到(O)的距离相同,那么我们先将它们都计算,然后再减去多计算的部分即可,可以证明这样的点的个数是(O(n))的。
#include<cmath>
#include<cstdio>
#include<algorithm>
using db=double;
int read(){int x;scanf("%d",&x);return x;}
const int N=50007;const db eps=1e-10;
db x,y,k[N],b[N],ans;int n,c,t[N*2],las[N],L[N*2],R[N*2];
struct node{db ang;int id;}a[N*2];
db sqr(db a){return a*a;}
void add(int p,int v){for(;p<=c;p+=p&-p)t[p]+=v;}
int ask(int p){int s=0;for(;p;p^=p&-p)s+=t[p];return s;}
int check(db r)
{
db A,B,C,D,X,Y;int s=0;c=0;
for(int i=1;i<=n;++i)
{
A=sqr(k[i])+1,B=-2*x+2*k[i]*(b[i]-y),C=sqr(x)+sqr(b[i]-y)-sqr(r);
if(B*B<=4*A*C) continue;
D=sqrt(sqr(B)-4*A*C),A*=2;
X=(D-B)/A,Y=k[i]*X+b[i],a[++c]={atan2(Y-y,X-x),i};
X=(-D-B)/A,Y=k[i]*X+b[i],a[++c]={atan2(Y-y,X-x),i};
}
std::sort(a+1,a+c+1,[](const node&a,const node&b){return a.ang<b.ang;});
for(int i=1,id;i<=c;++i) las[id=a[i].id]? s+=ask(i)-ask(las[id]),add(las[id],-1),las[id]=0:(add(i,1),las[id]=i);
return s;
}
int pre[100010],nxt[100010],lst;
db cal(int i,int j)
{
db X=(b[j]-b[i])/(k[i]-k[j]),Y=X*k[i]+b[i];
return sqrt(sqr(X-x)+sqr(Y-y));
}
int main()
{
db l=0;int m,s=0;
n=read(),x=read()/1000.0,y=read()/1000.0,m=read();
for(int i=1;i<=n;++i) k[i]=read()/1000.0,b[i]=read()/1000.0;
for(db r=1e13,mid,i=100;i>0;--i) check(mid=(l+r)/2)<m? l=mid:r=mid;
check(l);
for(int i=1,j=0,p;i<=c;++i)
if(las[a[i].id])
{
for(p=j;p>las[a[i].id];p=L[p]) ++s,ans+=cal(a[i].id,a[p].id);
R[L[p]]=R[p],L[R[p]]=L[p];
if(p==j) j=L[j];
}
else R[j]=i,L[i]=j,j=i,las[a[i].id]=i;
printf("%.10lf",ans+l*(m-s));
}