題意:給出一個輪子,上面有一個隨着它轉動的傳感器在圓周上,給出一個指定距離m,和輪子向前行進的速度v以及輪子的半徑r。問讓傳感器通過該距離最少需要多少時間。
分析:首先我們列出傳感器行進距離與時間的輪子行進距離的關系:f(c) = c+r*sin(c/r)。其中c是距離,r是半徑。該公式表示傳感器從輪子正上方開始運行索性進的距離。
我們可以先去除一種情況,就是距離m是直徑2r的整數倍,那么傳感器在開始點和結束點所處的角度是相同的,無論從哪個角度開始所需的時間都是一樣的。
我們只考慮非整數倍的情況。
要看傳感器行進速度的變化,我們需要對這個函數f關於c進行求導。
求得導數是:f'(c) = 1+cos(c/r)。這就是傳感器速度的函數。而這個函數圖像與x軸所夾的面積就是行進距離。
那么,我們現在的任務就變成了在這個圖像上截取一段,使得與x軸所夾的面積是m,且要保證我們截取的橫向長度最短。(因為速度是一定的,距離和時間成正比)
假設我們截取了x=a到x=b。那么一定有f'(a)=f'(b)。如果兩者不等,例如f'(a)<f'(b),我只要將右邊緣向右平移一點點,保證面積不變的話,就需要將左邊緣向右平移更多。
因為我們如果把這個增量看成小長方體,如果左右平移量相等,f'(a)<f'(b)導致右邊進來的就比左邊進來的長方體高,那面積就變了。
既然f'(a)=f'(b),就一定a在圖像的遞增區域,b在遞減區域,否則還會出現上面的情況,或者就是m是2r的整數倍。
也就是說兩邊緣必須對稱,也就是說選區的中間是最高點或者最低點。我們只需要對兩種情況分別進行二分查找即可。
二分查找的時候查找輪子的行進長度,每次觀察f(c)>=m/2是否成立。
二分查找有個需要特別注意的地方。如果每次只是限定L和R的差值是不行的。差值設大了就會出現WA,設小了會TLE。
需要限定二分迭代的次數為50次左右。不知道是不是其他的比賽或者OJ也有這種情況。

#include <cstdio> #include <algorithm> #include <cmath> using namespace std; #define d(x) #define zero(x) (((x)>0?(x):-(x))<eps) #define eps 1.0E-8 int double_cmp(double a) { if (zero(a)) return 0; return a > 0 ? 1 : -1; } int n, radius, v; int dist; bool top_ok(double bike_dist) { double ret = bike_dist + radius * sin(bike_dist / radius); return double_cmp(ret * 2.0 - dist) >= 0; } bool bottom_ok(double bike_dist) { double ret = bike_dist - radius * sin(bike_dist / radius); return double_cmp(ret * 2.0 - dist) >= 0; } double binary_search(bool (*ok)(double)) { double l = max(0.0, dist / 2.0 - 2 * radius); double r = dist / 2.0 + 2 * radius; int cnt = 0; while (double_cmp(r - l) > 0) { double mid = (l + r) / 2; if (ok(mid)) r = mid; else l = mid; cnt++; if (cnt > 100) break; } d(printf("%.2f\n", l / radius / 2 / 3.14)); return l; } double work() { double time_top = binary_search(&top_ok) / v; double time_bottom = binary_search(&bottom_ok) / v; d(printf("%.2f %.2f\n", time_top, time_bottom)); return min(time_top, time_bottom) * 2; } int main() { scanf("%d", &n); scanf("%d%d", &radius, &v); for (int i = 0; i < n; i++) { int a, b; scanf("%d%d", &a, &b); dist = b - a; printf("%.12f\n", work()); } return 0; }