ZOJ 3728 Collision

2013长沙区域赛的题目


题意:

一个硬币,半径为r,从(x,y)出发,速度(vx,vy)。 在(0,0)位置处有一个固定的,半径为Rm的实心圆。硬币碰到它后会无能量损耗反射。还有一个半径为R的范围,圆心也在(0,0)。硬币开始运动,求硬币有多少时间会呆在范围R内(硬币任何一部分在内都算)。


思路:

我们观察一下硬币与实心圆的圆心距随时间的变化,肯定是从大到小再变大。当然也有可能一直变大,一开始就直接远离。

我们可以三分出圆心距离最近的那个时间,设为limit。

然后在[0,limit]区间内距离都是增大的。我们再二分出,距离达到(R+r)的时间点(也就是将要进入范围R内),再二分出(Rm+r)的时间点(碰撞实心圆),因为反射无能量损耗,运动是对称的,所以两个时间点差值的两倍就是答案。


code:

#include <algorithm>
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <math.h>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
    
#define N  100010
#define ll long long
#define ALL(x)     x.begin(),x.end()
#define CLR(x,a)   memset(x,a,sizeof(x))
typedef pair<int,int> PI;
const int    INF=0x3fffffff;
const int    MOD=1000000007;
const double EPS=1e-9;


int Rm, R, r, x, y, vx, vy;
double limit;

double dis(double _x,double _y){
	return sqrt(_x*_x+_y*_y);
}

double cal(double t){
	return dis(t*vx+x,t*vy+y);
}

double getNearest(){
	double _l=0.0,_r=INF;
	int Time=1<<6;
	while(Time--){
		double L=(_l+_r)/3.0;
		double R=L*2.0;
		if(cal(L)<cal(R)) _r=R;
		else _l=L;
	}
	return (_l+_r)/2.0;
}

double find(int target){
	double _l=0.0, _r=limit;
	int Time=1<<6;
	while(Time--){
		double mid=(_l+_r)/2.0;
		if(dis(mid*vx+x,mid*vy+y)>=target) _l=mid;
		else _r=mid;	
	}
	return (_l+_r)/2.0;
}

int main(){
	while(~scanf("%d%d%d%d%d%d%d",&Rm, &R, &r, &x, &y, &vx, &vy)){
		limit=getNearest();
		double a=find(Rm+r), b=find(R+r);
		if((a-b)<EPS) puts("0.000");
		else printf("%lf\n",(a-b)*2.0);
	}
	return 0;
}


你可能感兴趣的:(ZOJ)