[luogu1742]最小圆覆盖
考虑维护包含前$i$个点的最小圆,并不断加入下一个点——
若加入的点被该圆包含,显然答案不变,否则该点必然在新的最小圆边界上
换言之,此时得到了一个确定边界上某点的子问题,并用类似的方式处理
以此类推,当第$3$轮中出现此情况时,即得到了圆边界上的三点,进而解出该圆
具体的,以距离圆心相等建立方程,化简后为二元一次方程组
同时,注意到更新后圆上至多含有$3$点,因此第$i$个点更新的概率不超过$\frac{3}{i}$
归纳每一轮的复杂度均为$o(n)$,代入即$o(n)+\sum \frac{3}{i}o(i)=o(n)$,显然成立
时间复杂度为$o(n)$(期望),可以通过

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define eps 1e-9 5 int n;mt19937 rnd(0); 6 struct Point{ 7 double x,y; 8 Point operator + (const Point &k)const{ 9 return Point{x+k.x,y+k.y}; 10 } 11 Point operator - (const Point &k)const{ 12 return Point{x-k.x,y-k.y}; 13 } 14 Point operator * (const double &k)const{ 15 return Point{x*k,y*k}; 16 } 17 double operator * (const Point &k)const{ 18 return x*k.y-y*k.x; 19 } 20 double len(){ 21 return sqrt(x*x+y*y); 22 } 23 double Len(){ 24 return x*x+y*y; 25 } 26 }a[N]; 27 struct Circle{ 28 double r;Point o; 29 bool check(Point &k){ 30 return (o-k).len()<=r+eps; 31 } 32 }ans; 33 Circle get_cir(Point &a,Point &b){ 34 return Circle{(a-b).len()/2,(a+b)*0.5}; 35 } 36 Circle get_cir(Point &a,Point &b,Point &c){ 37 Point A=(b-a)*2,B=(c-a)*2;double s=1/(A*B); 38 Point C=Point{b.Len()-a.Len(),c.Len()-a.Len()}; 39 Point o=Point{C*Point{A.y,B.y},Point{A.x,B.x}*C}*s; 40 return Circle{(o-a).len(),o}; 41 } 42 int main(){ 43 scanf("%d",&n); 44 for(int i=1;i<=n;i++)scanf("%lf%lf",&a[i].x,&a[i].y); 45 shuffle(a+1,a+n+1,rnd); 46 for(int i=1;i<=n;i++) 47 if (!ans.check(a[i])){ 48 ans=Circle{0,a[i]}; 49 for(int j=1;j<i;j++) 50 if (!ans.check(a[j])){ 51 ans=get_cir(a[i],a[j]); 52 for(int k=1;k<j;k++) 53 if (!ans.check(a[k]))ans=get_cir(a[i],a[j],a[k]); 54 } 55 } 56 printf("%.9f\n%.9f %.9f\n",ans.r,ans.o.x,ans.o.y); 57 return 0; 58 }