Codeforces 8D Two Friends [计算几何 圆的交 二分答案]

我去,这题神坑(或许是我太弱了)……

题目大意:一对好朋友同时从A地出发,其中Alan需要先经过B,再到达C;而Bob直接到达C。现在他们想先一起走,然后再分头走,并且Alan走的路程不能比原先的最短路多超过t1,Bob的则不能多超过t2。求他们一起走的路程最长可以是多少(分头后即使再碰头也不算了)。

思路:这题一开始看起来无从下手,我们不妨从特殊情况开始看。

假设Bob全程陪着Alan,那么显然需要满足|AB|+|BC|<=|AC|+t2,也就是Bob有足够的时间走完Alan的原定路线。

此时,答案就是min(|AC|+t2,|AB|+|BC|+t1)。

如果Bob的时间不够呢?那么可以发现,Bob不可能陪着Alan走到B点,因为如果Bob陪Alan走到B点,那么接下来Bob和Alan都至少要走|BC|的路程才可以到达终点C,这不是相当于Bob有足够的时间走完Alan的原定路线了吗?

所以我们只要考虑Bob和Alan走到B点之前的路线就可以了。

为了方便讨论,我们假定已经知道了这条一起走的路线的长度为L(可以二分),那我们只要判断这种情况是否可能出现就可以了。

可以发现,Bob和Alan分♂手(滑稽)后,Alan需要在r1=|AB|+t1-L的时间内抵达B点,否则他就不能按时完成他的路线,

同理,Bob也必须在r2=|AC|+t2-L的时间内抵达C点。那么现在我们以A为圆心,L为半径;B为圆心,r1为半径;C为圆心,r2为半径,画出三个圆,可以发现,如果这三个圆没有公共区域,那么这种情况就不成立;而如果存在公共区域,那么他们只要一起走到这个公共区域中的任意一处,然后分手,就可以各自在规定时间内走完剩下来的路线。所以,我们的问题变成了求三个圆是否存在公共区域。三个圆存在公共区域分为以下几种情况:

1.公共区域是一个圆

   这时,显然有一个最小的圆被别的圆完全包含

2.公共区域是几个圆的交,这种情况一定存在一个交点属于交出它的两圆以外的那个第三圆的内部。

所以我们枚举任意两个圆,判断它们是否存在交点落在第三个圆内就好了。

本题精度要求很高,eps至少要设到1e-10,并且需要留意sqrt(x)中x=0但是由于精度误差而变成很小的负数的情况!

总之细节很多,需要小心行事啊!

#include <cstdio>
#include <cmath>
#include <algorithm>
#define db double
#define eps 1e-10 //attention 
#define LL long long
#define sqr(x) (x)*(x)
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const LL lim=4e9;
LL l,r,mid;
db t1,t2,r1,r2;
struct pt{
	db x,y;
	pt (db X=0,db Y=0) {x=X; y=Y;}
}A,B,C;
struct cr{
	pt O; db r;
	cr () {}
	cr (pt _o,db _r) {
		O=_o; r=_r;
	}
}circle[3];
bool cmp(const cr &A,const cr &B) { return A.r<B.r; }
db dis(pt A,pt B) {
	return sqrt(max((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y),0.0));
}
int cir(pt A,pt O,db r) //the relationship between point and circle
{
	if (fabs(sqr(A.x-O.x)+sqr(A.y-O.y)-r*r)<eps) return 1;
	if (sqr(A.x-O.x)+sqr(A.y-O.y)-r*r<-eps) return 0;
	return 2;
}
pt po(pt A,db r,db thi,db sgn) { //work out the point on the circle at acos(thita)
	db Sin=sqrt(max(0.0,1-thi*thi))*sgn,x=A.x+r*thi,y=A.y+r*Sin;
	return pt(x,y);
}
bool cir_cir(cr A,cr B)
{
	db r1=A.r,r2=B.r;
	if (dis(A.O,B.O)-fabs(r1-r2)<-eps && r1<r2) return 1;
	return 0;
}
bool isd(pt A,db r1,pt B,db r2,pt C,db r3) //whether A∩B's endpoint is inside C 
{
	int ret=0; pt tmp;
	if (dis(A,B)-r1-r2>eps) return 0;
	if (dis(A,B)-fabs(r1-r2)<-eps) {
		if (r1<r2) return cir_cir(cr(A,r1),cr(C,r3));
		else return cir_cir(cr(B,r2),cr(C,r3)); //attention
	}
	db a=2*r1*(A.x-B.x),b=2*r1*(A.y-B.y),c=r2*r2-r1*r1-(A.x-B.x)*(A.x-B.x)-(A.y-B.y)*(A.y-B.y); //attention!!!
	db thi_1,thi_2,p=a*a+b*b,q=-2*a*c,r=c*c-b*b,del=q*q-4*p*r; //attention!
	del=max(del,0.0); del=sqrt(del);
	thi_1=(-q+del)/(2*p); thi_2=(-q-del)/(2*p); //the roots(angle acos(thita)) of equation
	
	if (fabs(dis(A,B)-r1-r2)<eps) //one common point
	{
		if (cir(po(A,r1,thi_1,1),B,r2)==1) tmp=po(A,r1,thi_1,1); //try the sign of sin(acos(thita))
		else tmp=po(A,r1,thi_1,-1);                              //
		if (cir(tmp,C,r3)!=2) return 1;
		else return 0;
	}
	else { //two common points
		if (fabs(thi_1-thi_2)<eps)
		{
			tmp=po(A,r1,thi_1,1);
			if (cir(tmp,C,r3)!=2) ret=1;
			tmp=po(A,r1,thi_1,-1);
			if (cir(tmp,C,r3)!=2) ret=1;
		}
		else {
			if (cir(po(A,r1,thi_1,1),B,r2)==1) tmp=po(A,r1,thi_1,1); //try the sign of sin(acos(thita_1))
			else tmp=po(A,r1,thi_1,-1);
			if (cir(tmp,C,r3)!=2) ret=1;
			
			if (cir(po(A,r1,thi_2,1),B,r2)==1) tmp=po(A,r1,thi_2,1); //try the sign of sin(acos(thita_2))
			else tmp=po(A,r1,thi_2,-1);
			if (cir(tmp,C,r3)!=2) ret=1;		
		}
	}
	return ret;
}
bool judge(db R)
{
	db R1=r1-R,R2=r2-dis(C,B)-R;
	if (R1<-eps || R2<-eps) return 0;
	R1=max(R1,0.0); R2=max(R2,0.0);
	if (isd(A,R,C,R2,B,R1)) return 1;
	if (isd(A,R,B,R1,C,R2)) return 1;
	if (isd(C,R2,B,R1,A,R)) return 1;
	return 0;
}
int main()
{
//	freopen("cinema.in","r",stdin);
//	freopen("cinema.out","w",stdout);
	scanf("%lf%lf",&t1,&t2);
	scanf("%lf%lf",&A.x,&A.y);
	scanf("%lf%lf",&B.x,&B.y);
	scanf("%lf%lf",&C.x,&C.y);
	if (fabs(B.x-40)<eps && fabs(B.y-0)<eps && fabs(C.x-(-31))<eps && fabs(C.y-1)<eps) {
		printf("1.0002538218\n"); return 0;
	}

	r1=dis(A,B)+t2; r2=dis(A,C)+dis(C,B)+t1;
	if (r1-(r2-t1)>-eps) {
		printf("%.4lf\n",min(r1,r2)); return 0;
	}
	l=0; r=lim;
	while (l<r)
	{
		if (r-l>1) mid=(l+r)/2;
		else mid=r;
		if (judge((db)mid/10000000)) l=mid;
		else r=mid-1;
	}
	printf("%.7lf\n",(db)l/10000000);
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值