codeforces 309A Morning run 概率

http://codeforces.com/problemset/problem/309/A
题意:
给定n 个人,给定长度为 l 的环形跑道,同时开始跑 t 时间,求相撞次数的期望,注意相撞之后,两个人还是按照原来的方向跑下去。
分析:
如果两个人是同向的话肯定是不可能相撞的
1)如果时间无限的话,那么任意两个人相撞的时间的就是 x/2 x/2+l/2 x/2+l …. 并且可以知道两个人相撞的概率就是0.5*0.5->0.25。
2)对于如果 t=l/2 的时候,在这个时间周期里,任意两个人会相撞一次,而这两个人的方向只需要相对就可以了。概率是0.5。
思路:
t=kl/2+p/2 -> 2t=kl+p ,k表示有几个时间周期为 l/2 ,p就是剩下的。对于k的话,运用上面的分析,每次任意两个人都会撞到1次。总次数为 kn(n1)/2 ,概率都为0.5。
剩下p秒,运用分析中的第一种,对于某两个人,如果 p/2<x/2 则撞不到,如果 x/2<=p/2 -> p>=x 撞到一次,如果 p/2>=x/2+l/2 撞到两次 但是这里会发现 p/2<l/2 是一定的,所以按照这样分解剩下的p秒不可能撞到两次。
因为我们将时间分解成了 l/2 为周期的,这样可以去掉剩下的时间碰撞两次的情况。
现在考虑如何快速的判断剩下p秒中任意两个人能否多撞1次。
将原本的数组扩展,在后面i+n的位置接着存下a[i]+l的值。这样就将两个不同的方向也归一了,举个例子比如 l = 7 两个人 0 , 3, 这其实是有两个距离的一个是3,一个是4,但是运用这样的方法就可以 判断a[0] 与 a[1]的关系解决了一个方向,判断a[1] 与 a[2] 就解决了另一个方向的问题。
尾指针枚举每一个人i -> 0~n,头指针0~2*n (或者0~i+n,不过对于每次i 一定不会超过 i+n ,因为p < l)往前移动 直到不能多撞一次,将次数加上可以多撞一次的个数。

#include <cstdio>
#include <algorithm>
#define M 10000009
int a[M];
int main()
{
    int n,l,t;
    while(scanf("%d %d %d",&n,&l,&t) == 3)
    {
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n] = a[i] + l;
        }
        int k = 2*t/l;
        int p = 2*t%l;
        double ans = (double)k*n*(n-1);
        for(int i = 0,j = 0;i < n;i++)
        {
            while(j < 2*n && a[j] - a[i] <= p)
                j++;
            ans += j-i-1;
        }
        printf("%.10f\n",ans/4);
    }
}

之前是写的是分解成 t=kl+p 的,应该也是可以的,就是以前写的有思维漏洞,但是水过了数据,将以前的修改了下。。
将原本的数组扩展,在后面i+n的位置接着存下a[i]+l,i+2n 位置存下a[i]+2l的值。就可以将问题都归一到能否多撞一次中,并且将两个不同的方向也归一了,据上面的例子比如 l = 7 两个人 0 , 3, 这其实是有两个距离的一个是3,一个是4,但是运用这样的方法就可以 判断a[0] 与 a[1],与a[3] 的关系,这样就覆盖了一次和两次的情况,判断a[1] 与 a[2],与a[4]就解决了另一个方向一次和两次的问题。。。弱菜求指正,严重觉得这样搞可能还有问题。还是分解成 2t=kl+p 将问题更简单的处理

#include <cstdio>
#include <algorithm>
#define M 10000009
int a[M];
int main()
{
    int n,l,t;
    while(scanf("%d %d %d",&n,&l,&t) == 3)
    {
        for(int i = 0;i < n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n] = a[i] + l;
            a[i+2*n] = a[i+n] + l;
        }
        int k = t/l;
        int p = t%l;
        double ans = (double)2*k*n*(n-1);
        for(int i = 0,j = 0;i < n;i++)
        {
            while(j < 3*n && a[j] - a[i] <= 2*p)
                j++;
            if(j > i+n) //i+n 与 i 实际为同一个点
            ans += j-i-1-1;
            else
            ans += j-i-1;
        }
        printf("%.10f\n",ans/4);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值