Codeforces Round #357 (Div. 2) E. Runaway to a Shadow

E. Runaway to a Shadow

time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Dima is living in a dormitory, as well as some cockroaches.

At the moment 0 Dima saw a cockroach running on a table and decided to kill it. Dima needs exactly T seconds for aiming, and after that he will precisely strike the cockroach and finish it.

To survive the cockroach has to run into a shadow, cast by round plates standing on the table, in T seconds. Shadow casted by any of the plates has the shape of a circle. Shadow circles may intersect, nest or overlap arbitrarily.

The cockroach uses the following strategy: first he equiprobably picks a direction to run towards and then runs towards it with the constant speed v. If at some moment t ≤ T it reaches any shadow circle, it immediately stops in the shadow and thus will stay alive. Otherwise the cockroach is killed by the Dima’s precise strike. Consider that the Dima’s precise strike is instant.

Determine the probability of that the cockroach will stay alive.
Input

In the first line of the input the four integers x0, y0, v, T (|x0|, |y0| ≤ 109, 0 ≤ v, T ≤ 109) are given — the cockroach initial position on the table in the Cartesian system at the moment 0, the cockroach’s constant speed and the time in seconds Dima needs for aiming respectively.

In the next line the only number n (1 ≤ n ≤ 100 000) is given — the number of shadow circles casted by plates.

In the next n lines shadow circle description is given: the ith of them consists of three integers xi, yi, ri (|xi|, |yi| ≤ 109, 0 ≤ r ≤ 109) — the ith shadow circle on-table position in the Cartesian system and its radius respectively.

Consider that the table is big enough for the cockroach not to run to the table edges and avoid Dima’s precise strike.
Output

Print the only real number p — the probability of that the cockroach will stay alive.

Your answer will be considered correct if its absolute or relative error does not exceed 10 - 4.
Examples
Input

0 0 1 1
3
1 1 1
-1 -1 1
-2 2 1

Output

0.50000000000

Input

0 0 1 0
1
1 0 1

Output

1.00000000000

链接 http://codeforces.com/contest/681/problem/E

题意

给你一个起点,告诉你最远能走的距离。再给出n个圆,问随机选择一个方向走,能走到给出的任意一个圆的概率是多少(到达后就停止,不会穿过圆)。

思路

高中几何问题,起点在圆内时答案为1,否则起点到给定圆会有两条切线,当随机选择的角度在两条切线所夹锐角内,且能走的距离+圆的半径小于等于起点到圆心的距离时,满足题意。将所有的可行的锐角取并集后的总角度除以2π即为答案。

代码

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <map> 
using namespace std;
double pi=asin(1.0)*2.0;
map<double,int>a;
map<double,int>::iterator it;
double area(double a,double b,double c)
{
    double p=(a+b+c)/2;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
double angle(double a,double b,double c)
{

    double ans,s,h;
    s=area(a,b,c);
    h=2*s/b;
    ans=asin(h/a);
    return ans;
}
int main()
{
    int n;
    long long x0,y0,v,t,r0,x,y,r;
    double p,q,ans=0;
    scanf("%I64d %I64d %I64d %I64d",&x0,&y0,&v,&t);
    r0=v*t;
    scanf("%d",&n);
    for (int i=0;i<n;i++)
    {
        scanf("%I64d %I64d %I64d",&x,&y,&r);
        x=x-x0;
        y=y-y0;
        if (r0>3000000000ll || (r0+r)*(r0+r)>=x*x+y*y) //r0直接求平方会爆long long  
        {
            if (r*r>=x*x+y*y) ans=1.0;
            else
            {
                if (x==0)
                {
                    if (y>0) p=pi/2;
                    else p=-pi/2;
                }
                else
                {
                    p=atan(((double)y)/x);
                }
                if (r0>3000000000ll || x*x+y*y-r*r<=r0*r0) q=angle(sqrt(x*x+y*y-r*r),sqrt(x*x+y*y),r);
                else q=angle(r0,sqrt(x*x+y*y),r);
                if (x<0) q+=pi;
                a[ p+q>=3*pi/2 ? p+q-2*pi : p+q]-=1;
                a[ p-q<-pi/2 ? p-q+2*pi : p-q]+=1;
            }

        }
    }
    if (ans)
    {
        printf("%.12lf",ans);
        return 0;
    } 
    int w=0,k=0;
    for (it=a.begin();it!=a.end();it++)
    {
        w+=it->second;
        k=min(k,w);
    }
    k=-k;
    double be,en;
    be=-pi/2;
    for (it=a.begin();it!=a.end();it++)
    {
        if (k==0) be=it->first;
        k+=it->second;
        if (k==0)
        {
            en=it->first;
            ans+=en-be;
        }
    }
    if (k!=0)
    {
        en=3*pi/2;
        ans+=en-be;
    }
    ans/=2*pi;
    ans=min(1.0,ans);
    printf("%.12lf",ans);
}

PS

来来回回写了好多遍,测试中不断有特殊情况的数据WA,CF给出测试数据真是太人性了。这里总结一下,这题本来直接包括坐标点所有数据都使用double,但是当b-a时,如果a,b很接近,(b-a)远远小于a时,由于double精度问题会造成结果为0,这样使得我在使用asin()函数算角度时出现除以0的错误。后来将坐标点使用long long存储,这时又要处理题目中v*t最大为1e18,自身平方会造成爆精度上限,所以还需要判特。
总的来说这题题目很简单,但是代码实现比较繁琐,值得反思的是今后再遇到几何等需要开根号的问题,不要图省事全用double

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值