1. 程式人生 > >Nature Reserve(Codeforces Round #514 (Div. 2)-1060E)(三分答案,數學)

Nature Reserve(Codeforces Round #514 (Div. 2)-1060E)(三分答案,數學)

文章目錄

前言

這種題沒做過,但考試時想得差不多了…

題目

CF傳送門
題目大意
現在有n個點,座標分別為(xi,yi)(x_i,y_i),現在求一個最小半徑的圓,使得包含所有點且和xx軸相切(只有一個交點),
若不存在,輸出-1
答案精確到10610^{-6}
107<=xi,yi<=107,yi!=0-10^7<=x_i,y_i<=10^7,y_i!=0
inputinput

1
0 1

outputoutput

0.5

in

putinput

3
0 1
0 2
0 -3

outputoutput

-1

inputinput

2
0 1
1 1

outputoutput

0.625

思路

首先,我們知道,只要x軸兩側各有點,則輸出-1.
我們記圓心為(X,r)(X,r)由於和x軸相切,則半徑為r,對於每一個點(xi,yi)(x_i,y_i)
圖片
都有:
(Xxi)2+(ryi)2<=r2(X-x_i)^2+(r-y_i)^2<=r^2
我們只能從化簡式子入手了:
X2

2xiX+xi2+r22ryi+yi2<=r2X^2-2x_iX+{x_i}^2+r^2-2ry_i+{y_i}^2<=r^2
化簡一下,把r移到左邊:
X22xiX+xi2+yi22yi<=r\frac{X^2-2x_iX+{x_i}^2+{y_i}^2}{2y_i}<=r
也就是說,如果我們有確定的XX,那麼:
r=max{(Xxi)2+yi22yi}r=max\{\frac{(X-x_i)^2+{y_i}^2}{2y_i}\}
,我們就有確定的rr
我們又發現,對於答案所在的X,在它左右走rr都會單調遞增,形成形狀像山谷的形狀,那麼直接三分XX找谷底即可

程式碼

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
LL read(){
    LL f=1,x=0;char s=getchar();   
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
#define eps 1e-8
#define MAXN 100000
#define INF 0x3f3f3f3f
#define Mod int(1e9+7)
int n;
LL x[MAXN+5],y[MAXN+5];
double check(double X){
	double r=0;//半徑計算
	for(int i=1;i<=n;i++)
		r=max(r,((X-x[i])*(X-x[i])+y[i]*y[i])/(2.0*y[i]));
	return r;
}
int main(){
	int f=0;
	n=read();
	for(int i=1;i<=n;i++){
		x[i]=read(),y[i]=read();
		if(y[i]<0) y[i]=-y[i],f|=1;
		else f|=2;
	}
	if(f==3){
		puts("-1");
		return 0;
	}
	double L=-1e7-2,R=1e7+2;
	while(L+eps<R){//三分橫座標
		double Mid1=L+(R-L)/3,Mid2=R-(R-L)/3;
		double tmp1=check(Mid1),tmp2=check(Mid2);
		if(tmp1>tmp2) L=Mid1;//左邊半徑較大,L右移動
		else R=Mid2;
	}
	printf("%.10lf\n",check(L));
	return 0;
}