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=k∗l/2+p/2
->
2t=k∗l+p
,k表示有几个时间周期为
l/2
,p就是剩下的。对于k的话,运用上面的分析,每次任意两个人都会撞到1次。总次数为
k∗n∗(n−1)/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=k∗l+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=k∗l+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);
}
}