题解:CF2172B Buses

· · 题解

结论题。

题解中变量名与题面中有不同:s,t\rightarrow l,r;\; x,y\rightarrow v_0,v_1;\;\;l\rightarrow c

翻译(简化)

一条路长 c。有 n 辆均以 v_0 的速度向右匀速运动(即每秒向右走 v_0 单位长度)的列车,均在零时刻发车,第 i 辆发自 l_i 终于 r_i。(位置 p 表示距路左端点 p。)

m 人,第 i 个人从位置 p_i 出发到路右端点(位置 c),可以 v_1 的速度行走,亦可乘车:若某时刻与某列车位置相同,则可上车;若已上车,可随时下车,但车到终点必须下车。(可不在整数时刻与位置上车。)

求出每个人到达终点的最短时间。以小数输出,要求相对相对误差或绝对误差小于 10^{-6}

数据范围:1\le n,m\le 2\times 10^5,1\le c\le 10^9,1\le v_1<v_0 \le 10^6;0\le l_i<r_i<c,0\le p_i\le c

题解

试试新渲染特性,以效 CF 题解之风。(注:建议按照数字 0、1、2、3、4 的顺序先点开提示,独立思考片刻。解答是该提示对应的步骤,解答 0、1、2、3、4 顺序连接构成完整的题解)

:::info[提示 0.0] 所有的车速度一样。 ::: :::info[提示 0.1] 对每个人分析,他能上哪些列车? ::: :::success[解答 0]

所有起点在位置 p 之左(含 p),终点位置在 p 之右的列车都可搭乘。

即若 l_i\le p_j<r_i,则第 j 个人可搭乘列车 i

由于所有车平行运动(速度一样),所以不可能出现超车现象(乘一辆车追上另一辆车),于是在 p 之右的列车,是不可能出现乘一辆车追上的。当然走路追就更不可能了。(v_0>v_1

所以只有起点在位置 p 之左(含 p)的列车可搭乘。 ::: :::info[提示 1.0] 车不等人,只会按照既定轨迹准时达到终点。 ::: :::info[提示 1.1] 结论:最多上一辆列车。想想为什么? ::: :::success[解答 1]

假设要上两辆车,不如只上右边那辆车。因为无论上不上前一辆车,都能上后一辆车(所有在 p 前的皆可上)。而无论是否上前一辆,到达终点的时间不变。 ::: :::info[提示 2] 求出若乘车 i ,到达右端点 c 的时间。 ::: :::success[解答 2] 显然坐车应该坐到终点,因为走路不如坐车快。

车走了 r_i-l_i 的路程,由速度公式,在 \dfrac{r_i-l_i}{v_0} 时刻到达终点。

之后走路,从 r_ic,用时 \dfrac{c-r_i}{v_1}

相加,通分,总用时 \dfrac{(r_i-l_i)v_1+(c-r_i)v_0}{v_0v_1}。 :::

::::success[解答 3] 在所有能坐上的车中,应选取一个用时最短的。

(易错点:还可以纯走路。用时 \dfrac{c-p_j}{v_1}。不过这个到输出结果时取最小值就好了。)

所以题目转换为了:有 m 个询问,每个询问给出 p_j ,要在所有 l_i\le p_j<r_ii 中求出 \dfrac{(r_i-l_i)v_1+(c-r_i)v_0}{v_0v_1} 最小值。

:::: :::info[提示 4.0]

::: :::success[解答 4.0] 如果 $p_j\ge r_i$,那么坐了这辆车,不仅用了时间,而且还退后了,肯定不优,会在取最小值时自动舍去,所以可以忽略 $p_j<r_i$ 的条件。 ::: :::info[提示 4.1] 去掉这个条件后,越往右,能坐的车就越多。 ::: :::info[提示 4.2] 很自然地想到,给每个位置都记录最小乘车时间就好了。但这样会超时:$c\le 10^9$。 ::: :::info[提示 4.3] 记录必要的即可。 ::: :::success[解答 4.3] 显然,只有 $l_i$ 所在位置,才有可能导致能乘的车改变。所以只需要对于每个 $i$,记录从 $l_i$ 出发最小乘车时间(不考虑纯走路所用的最短时间)。 遂按 $l_i$ 排序,从左到右依次求最小乘车时间。 设 $zyl_i$ 表示从 $l_{i-1}$ 到 $l_{i}-1$ 这段区间内出发的点,最小乘车时间。可推出状态转移方程:$zyl_0=\infin,zyl_i=\min(zyl_{i-1},\dfrac{(r_i-l_i)v_1+(c-r_i)v_0}{v_0v_1})\;\;(i\ge1)$。(注:$zyl_0=\infin$ 是因为在 $l_0$ 左边的乘不到车,这样与纯走路取 $\min$ 时自动舍去。) 然后对每个人,二分查找最大的 $i$ 使 $l_i<p_j$ 即可。(离线排序再双指针亦可。) ::: ## 代码(C++) 为运算方便,将时间都乘以了 $v_0v_1$ 以变成整数。最后输出时再除 $v_0v_1$。 ```cpp #include<iostream> #include<algorithm> using namespace std; struct xyq{ int l,r; }a[1000005]; struct rule{ //特殊比较方式。为了按照 $l$ 排序。 bool operator()(const xyq &s0,const xyq &s1){ return s0.l<s1.l; } }; long long zyl[1000005]; int main(){ int n,m,l,i,j; long long v0,v1; xyq tkr; cin>>n>>m>>l>>v0>>v1; for(i=0;i<n;i++){ scanf("%d %d",&a[i].l,&a[i].r); } sort(a,a+n,rule()); zyl[0]=2012345678987654321ll; for(i=1;i<=n;i++){ zyl[i]=min(zyl[i-1],(a[i-1].r-a[i-1].l)*v1+(l-a[i-1].r)*v0); } for(i=0;i<m;i++){ scanf("%d",&tkr.l); //下面一行,zyl[upper_bound(a,a+n,tkr,rule())-a] 是指最小的乘车时间,(l-tkr.l)*v0 是纯走路所用时间,两者取 min。 printf("%.8f\n",(double)min(zyl[upper_bound(a,a+n,tkr,rule())-a],(l-tkr.l)*v0)/(v0*v1)); //这里 upper_bound 是返回大于 tkr 的最小位置,返回值是一个指针,所以减去起始位置。大于 tkr 的前一个位置就是小于 tkr 的最大位置。由于 a 数组从 0 开始编号,本身就左移了 1 位,就不用再减一了。 } return 0; } ``` :::warning[易错点:浮点数输出] ```printf("%f",...);``` 只会保留 $6$ 位有效数字。本题要求误差在 $10^{-6}$ 以内,所以会 [WA](https://codeforces.com/contest/2172/submission/350146794)。使用 ```"%.8f"``` 保留 $8$ 位小数就能满足精度要求了。