题解 P3517 【[POI2011]WYK-Plot】
显然的二分答案。
对于一个二分出的答案
求一段点的最小圆覆盖,用随机增量法可以做到
所以难点就是怎么写
以找第一个连续的最长段为例,当然可以一个一个枚举过去找到最远的右端点,但是发现如果第一段就是
考虑优化一下,对于一个左端点,可以二分一下右端点,这样看起来复杂度更优一点,但是考虑如果每一段长度都为
发现复杂度瓶颈在于每次二分得到的区间长是
这题好像很卡精度,随机增量法用不同的随机种子还会
总复杂度
#include<bits/stdc++.h>
#define ts cout<<"ok"<<endl
#define ll long long
#define hh puts("")
#define pc putchar
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
using namespace std;
const int N=100005;
int n,m,res[N][2],cnt,ci;
double eps=1e-10,R;
struct point{
double x,y;
}a[N],b[N],O;
inline int read(){
int ret=0,ff=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') ff=-ff;ch=getchar();}
while(isdigit(ch)){ret=(ret<<3)+(ret<<1)+ch-'0';ch=getchar();}
return ret*ff;
}
void write(int x){
if(x<0){x=-x;putchar('-');}
if(x>9) write(x/10);
putchar(x%10+48);
}
void writeln(int x){write(x),hh;}
point Mid(point A,point B){
return (point){(A.x+B.x)/2,(A.y+B.y)/2};
}
double dist(point A,point B){
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
void getO(point A,point B,point C){
double aa,bb,cc,dd,ee,ff;
aa=A.x-C.x;bb=A.y-C.y;
cc=B.x-C.x;dd=B.y-C.y;
ee=A.x*A.x+A.y*A.y-C.x*C.x-C.y*C.y;
ff=B.x*B.x+B.y*B.y-C.x*C.x-C.y*C.y;
O.x=(dd*ee-bb*ff)/(2*aa*dd-2*bb*cc);
O.y=(aa*ff-cc*ee)/(2*aa*dd-2*bb*cc);
R=dist(O,A);
// printf("%.5lf %.5lf",O.x,O.y),hh;
// printf("%.5lf %.5lf %.5lf",dist(O,A),dist(O,B),dist(O,C));
}
void work(int l,int r){//随机增量求最小圆覆盖,O(r-l+1)
int tot=0;
for(int i=l;i<=r;i++) b[++tot]=a[i];
for(int i=1;i<=tot;i++) swap(b[i],b[rand()%tot+1]);
O=b[1],R=0;
for(int i=1;i<=tot;i++){
if(dist(b[i],O)>R+eps){//i不在圆内
O=b[i],R=0;
for(int j=1;j<i;j++){
if(dist(b[j],O)>R+eps){//j不在圆内
O=Mid(b[i],b[j]);
R=dist(O,b[i]);
for(int k=1;k<j;k++)
if(dist(b[k],O)>R+eps)
getO(b[i],b[j],b[k]);
}
}
}
}
}
bool check(double lim){
cnt=0;
int ans;
for(int i=1;i<=n;i=ans+1){
int k;
for(k=1;i+(1<<k)-1<=n;k++){//k=0显然可行,从1开始
work(i,i+(1<<k)-1);
if(R>lim+eps) break;
}
ans=i;
int l=i+(1<<(k-1))-1,r=min(n,i+(1<<k)-1);
while(l<=r){
int mid=(l+r)>>1;
work(i,mid);
if(R<lim+eps) l=mid+1,ans=mid;
else r=mid-1;
}
cnt++;
res[cnt][0]=i,res[cnt][1]=ans;
if(cnt>m) return 0;
}
return 1;
}
signed main(){
srand(20031128);
n=read(),m=read();
for(int i=1;i<=n;i++){
a[i].x=read();
a[i].y=read();
}
work(1,n);
double l=0,r=R;
if(m>1){
ci=50;
while(ci--&&r-l>eps){
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
}
check(r);
printf("%.8lf\n",r);
writeln(cnt);
for(int i=1;i<=cnt;i++){
work(res[i][0],res[i][1]);
printf("%.8lf %.8lf\n",O.x,O.y);
}
return 0;
}