题目大意

N个半径为R厘米的球竖直叠放在H米的高度上。实验开始时释放最下面的第一个球,1s后释放最下面的第二个球,1s后释放第三个球……一次类推。求T秒时每个球底端的高度。所有碰撞都是弹性碰撞, g取10 m/s^2

题目分析

这题的转化非常巧妙。

首先观察到很重要的一点,所有球的顺序在碰撞过程中都是不变的。很明显最下面一个球永远会在最下面,最上面一个球永远在最上面……这个很显然的结论将会给我们下面的转化过程带来帮助。

考虑两个球碰撞时的情形,根据物理知识,我们知道碰撞时的两个球的速度一定大小相同,方向相反。碰撞后交换速度。

这时候我们可以将两个球互相弹回的过程视作两个球“穿过了对方”而继续运动。在穿过的这个瞬间,球的速度没有变化,但下面的球高度获得2R的增量,上面的球高度获得-2R的增量。

然后,最关键的一步转化来了。

如果我们修改每个球的高度的定义,将每个球的高度h[i]定义为实际高度-2*i*R (球的坐标分别为0..N-1, 从下往上),这样会发生什么?

首先在初始时刻,所有的球的高度h[i]都是H。

然后我们再来看两个球碰撞的过程,碰撞时“穿过了对方”也就是“带着自己的速度到了对方的位置”。

这样想的话,我们就相当于交换了两个相邻的球的顺序,与一开始我们的结论“所有球的顺序一直不变有点矛盾”。

我们加多一层转化,就是将碰撞过程视作“带着自己的速度到了对方的位置,并变成了对方”。比如i和i+1碰撞,我们看作,i保持着自己的速度瞬间跳转i+1的位置,并变成了i+1;i+1同理。

i变成i+1的话,高度的定义也发生了改变,从实际高度-2*i*R 变成 实际高度-2*(i+1)*R], 与实际高度的差值加多了一个2R, 另外这个过程里,i也会获得2R高度的增量,所以两部分抵消,h高度值完全没有变化。

这么一来,问题就变成,N个球相隔1s依次从高度H开始下落,求T秒后各球高度。另外我们要还原成实际高度,将高度排序后从下往上依次加上2*i*R即可.

就这样完美转化成一道简单物理题。

source code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const double g = 10;

double solve(double h, double t) {
double t0 = sqrt(2 * h / g);
t -= 2 * t0 * ((int)(t / (2 * t0)));
if (t > t0) t = 2 * t0 - t;
return h - 0.5 * g * t * t;
}

const int maxn = 200;
double a[maxn];

int main() {
int Test;
scanf("%d", &Test);
while (Test--){
int N;
double H, R, T;
scanf("%d%lf%lf%lf", &N, &H, &R, &T);
R /= 100;
for (int i = 0; i < N; i++) {
a[i] = H;
}
for (int i = 0; i < min((int)T,N); i++) {
a[i] = solve(a[i], T - i);
}
sort(a, a + N);
for (int i = 0; i < N; i++) {
printf("%.2lf%c", a[i] + 2 * i * R, (i == N - 1 ? 10:32));
}
}
}