题目大意:给定n个点和m,要求把n个点分成m段,使得每段的最小覆盖圆的半径的最大值最小
肯定能想到要二分答案,然后看看怎么判定
对于每个点来说,需要找到序列中最远能到达的从他开始符合条件的点
但是因为最小圆覆盖需要随机打乱点才能保证时间复杂度,所以我们不能依次推出最小覆盖圆,那么能想到二分
但是每次检验是O(长度)的,而我们需要二分m次,直接二分可能会使时间复杂度变成O(logINF*NMlogN)
所以我们需要用到一些小技巧,就是先检验长度为1的方案,然后检验长度为2的方案,然后4,8.....直到找到一个不合法的k
然后我们紧接着在[k/2,k]这个区间里二分就可以了
因为这样这次搜索的二分上限为O(KlogN),而对答案的贡献至少为K,所以当对答案的贡献为N(即扫到最后)时,时间复杂度上限为O(NlogN)
所以总时间复杂度变成了O(NlogNlogINF)
PS:这题在POI上单点220s!!!!!!!BZOJ总时限300s差评!!害的我卡了好几发OJ,最后调了调eps,300.198s卡时过了....
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 100010
#define eps 2e-8
using namespace std;
struct node{long double x,y;};
long double sqr(long double x){return x*x;}
long double dis(node x,node y)
{
return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));
}
node a[N],b[N];
int n,m;
node O;
long double R;
node jfc(long double a,long double b,long double c,long double d,long double e,long double f)
{
long double y=(a*f-c*d)/(b*d-a*e);
long double x=(b*f-c*e)/(a*e-b*d);
return (node){x,y};
}
bool incircle(node x)
{
if(dis(x,O)<=R+eps) return true;
return false;
}
void solve(int l,int r)
{
int i,j,k;
for(i=l;i<=r;i++)
b[i-l+1]=a[i];
int nn=r-l+1;
random_shuffle(b+1,b+nn+1);
R=0;
for(i=1;i<=nn;i++)
if(!incircle(b[i]))
{
O=b[i];R=0;
for(j=1;j<i;j++)
if(!incircle(b[j]))
{
O.x=(b[i].x+b[j].x)/2;
O.y=(b[i].y+b[j].y)/2;
R=dis(O,b[i]);
for(k=1;k<j;k++)
if(!incircle(b[k]))
{
O=jfc(
b[i].x-b[j].x,b[i].y-b[j].y,(sqr(b[j].x)+sqr(b[j].y)-sqr(b[i].x)-sqr(b[i].y))/2,
b[i].x-b[k].x,b[i].y-b[k].y,(sqr(b[k].x)+sqr(b[k].y)-sqr(b[i].x)-sqr(b[i].y))/2
);
R=dis(O,b[i]);
}
}
}
}
int far(int st,long double limit)
{
int i,j;
for(i=1;;i++)
{
solve(st,min(st+(1<<i)-1,n));
if(R>limit+eps) break;
if(st+(1<<i)-1>=n) return n;
}
int l=st+(1<<(i-1)),r=min(st+(1<<i)-1,n),mid;
while(l<r)
{
mid=(l+r)>>1;
solve(st,mid);
if(R>limit+eps) r=mid;
else l=mid+1;
}
return l-1;
}
bool judge(long double limit)
{
int i,j,tot=0;
for(i=1;i<=n;i=j+1)
{
if(tot==m) return false;
j=far(i,limit);
tot++;
}
return true;
}
int main()
{
/*freopen("wyk.in","r",stdin);
freopen("wyk.out","w",stdout);*/
scanf("%d%d",&n,&m);
int i,j;
double tmp1,tmp2;
for(i=1;i<=n;i++)
{
scanf("%lf%lf",&tmp1,&tmp2);
a[i].x=tmp1;a[i].y=tmp2;
}
solve(1,n);
long double l=0,r=R,mid;
while(r-l>eps)
{
mid=(l+r)/2;
if(judge(mid)) r=mid;
else l=mid;
}
printf("%.15lf\n%d\n",(double)l,m);
j=1;
for(i=1;i<=m;i++)
{
if(j>n){puts("0 0");continue;}
int tmp=far(j,l+eps);
solve(j,tmp);
printf("%.15lf %.15lf\n",(double)O.x,(double)O.y);
j=tmp+1;
}
}