我们先二分一下,问题转化成在一个圆上,有若干条弧,问最少在圆上安排多少个点,使得每段弧中至少有一个点。我们先排序去重。然后,通过简单的倍增得出。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef double db;
const db eps=1e-5,pi2=6.2831853071795864769252867665;
int n,m;
#define Maxn 40010
struct P{
int x,y;
db a,d;
void calc(){
a=atan2(y,x);
if(a<0)a+=pi2;
d=sqrt(x*x+y*y);
}
}p[Maxn];
struct Seg{
db l,r;
int id;
bool operator <(const Seg &z)const{return r==z.r?l>z.l:r<z.r;}
bool operator ==(const Seg &z)const{return l==z.l&&r==z.r;}
}seg[Maxn<<1];
int f[Maxn<<1][17];
bool cover[Maxn];
int cnt=0;
inline bool Judge(db r){
memset(cover,false,sizeof(cover));
for(int i=1;i<=n;++i){
db d=acos(r/p[i].d);
seg[i]=(Seg){p[i].a-d,p[i].a+d,i};
seg[i+n]=(Seg){p[i].a-d+pi2,p[i].a+d+pi2,i};
}
sort(seg+1,seg+2*n+1);
for(int i=1;i<2*n;++i)
if(seg[i+1].l<=seg[i].l){
cover[seg[i+1].id]=true;
seg[i+1]=seg[i];
}
cnt=0;
for(int i=1;i<=n;++i)
if(!cover[i]){
db d=acos(r/p[i].d);
seg[++cnt]=(Seg){p[i].a-d,p[i].a+d,i};
seg[++cnt]=(Seg){p[i].a-d+pi2,p[i].a+d+pi2,i};
}
sort(seg+1,seg+cnt+1);
for(int i=1,nex=1;i<=cnt;++i){
while(nex<=cnt&&seg[nex].l<=seg[i].r)nex++;
f[i][0]=nex;
}
f[cnt+1][0]=cnt+1;
for(int j=1;j<=16;++j)
for(int i=1;i<=cnt+1;++i)f[i][j]=f[f[i][j-1]][j-1];
int ans=(cnt>>1);
for(int i=1;i<=(cnt>>1);++i){
int res=0,u=i;
for(int j=16;j>=0;--j)
if(f[u][j]<i+(cnt>>1)){
u=f[u][j];
res|=(1<<j);
}
ans=min(ans,res+1);
}
return ans<=m;
}
int main(){
db l=0,r=1000000000.0;
scanf("%d%d",&n,&m);
for(register int i=1;i<=n;++i){
scanf("%d%d",&p[i].x,&p[i].y);
p[i].calc();
r=min(r,p[i].d);
}
while(r-l>eps){
db mid=(l+r)*0.5;
if(Judge(mid))l=mid;
else r=mid;
}
printf("%.2lf\n",r);
return 0;
}