1 条题解
-
0
题解:马拉松大会 2(Marathon Race 2)
核心结论:通过排序+前缀和预处理球的位置,枚举4种最优路径(覆盖所有最短时间可能),对每个查询 计算最小时间并与 比较,即可判断是否完赛。
一、关键观察与时间计算逻辑
1. 最优路径的核心性质
理惠的行动分为三部分:从起点 到第一个球、收集所有球、从最后一个球到终点 。收集所有球的最优路径必然是在球位置的左右端点之间往返(贪心思想),因为绕路会增加距离和时间。
设球的位置排序后为 ,定义左右端点 、(多个球在同一位置不影响,排序后仍为连续相同值)。
2. 时间计算拆解
总时间 = 移动时间 + 拿球时间(固定为 秒,每个球1秒)。
移动时间的核心规律:拿着 个球跑1米耗时 秒,收集第 个球后持球数为 ,因此:
- 从第 个球到第 个球()的移动时间 = $\sum_{k=a}^{b-1} (k+1) \times |x_{k+1} - x_k| = \sum_{k=a}^{b-1} (k+1) \times (x_{k+1} - x_k)$(排序后单调不减,绝对值可去掉)。
3. 4种候选最优路径
所有最优路径仅需考虑以下4种(覆盖从 出发、到 结束的所有最短可能):
- (仅当 在 左侧时可能更优)
- (仅当 在 右侧时可能更优)
二、预处理步骤()
1. 排序与端点计算
- 对所有球的位置 排序,得到 。
- 计算左右端点 、。
2. 前缀和预处理
定义两个前缀和数组,用于快速计算移动时间:
- :前 个球之间的移动时间(从 到 ),即 。
- :前 个球之间的“反向系数”和,即 $\sum_{k=1}^{i-1} (N - k + 1) \times (x_{k+1} - x_k)$(用于计算从 到 的移动时间)。
预处理公式:
- ;()。
- ;$sum2[i] = sum2[i-1] + (N - i + 2) \times (x_i - x_{i-1})$()。
最终:
- 从 到 的移动时间 = 。
- 从 到 的移动时间 = 。
三、查询处理( per query)
对每个查询 ,计算4种路径的总时间,若最小时间 则输出 Yes,否则 No。
1. 路径时间计算细节
设:
- (从起点出发,持球数0,跑1米耗时1秒)。
- (同上)。
- (收集完所有球,持球数 ,跑1米耗时 秒)。
- (同上)。
- (从 到 的移动时间)。
- (从 到 的移动时间)。
4种路径的总时间:
- 路径1:。
- 路径2:。
- 路径3:$t_{S\to L} + t_{L\to R} + t_{R\to L} + t_{L\to G} + N$(往返 )。
- 路径4:$t_{S\to R} + t_{R\to L} + t_{L\to R} + t_{R\to G} + N$(往返 )。
2. 特殊情况处理
- 当 :无需拿球,总时间 = (但题目中 ,可忽略)。
- 当 (所有球在同一位置):,路径简化为 ,总时间 = 。
四、代码实现
import java.io.*; import java.util.*; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); String[] line = br.readLine().split(" "); int N = Integer.parseInt(line[0]); int L = Integer.parseInt(line[1]); int[] X = new int[N]; line = br.readLine().split(" "); for (int i = 0; i < N; i++) { X[i] = Integer.parseInt(line[i]); } Arrays.sort(X); // 预处理前缀和 sum1 和 sum2 long[] sum1 = new long[N + 1]; // sum1[i]:x1到xi的移动时间 long[] sum2 = new long[N + 1]; // sum2[i]:xN到xi的反向移动时间(从右到左) for (int i = 2; i <= N; i++) { long dist = X[i - 1] - X[i - 2]; sum1[i] = sum1[i - 1] + (long) i * dist; // 第i-1步持球数为i-1,耗时(i-1)+1 = i sum2[i] = sum2[i - 1] + (long) (N - i + 2) * dist; // 反向时第i-1步持球数为N - (i-1),耗时(N -i +1)+1 = N -i +2 } long t_LR = sum1[N]; // L到R的时间 long t_RL = sum2[N]; // R到L的时间 int left = X[0]; int right = X[N - 1]; int Q = Integer.parseInt(br.readLine()); while (Q-- > 0) { line = br.readLine().split(" "); int S = Integer.parseInt(line[0]); int G = Integer.parseInt(line[1]); long T = Long.parseLong(line[2]); // 计算4种路径的时间 long t1 = (long) Math.abs(S - left) + t_LR + (long) (N + 1) * Math.abs(right - G) + N; long t2 = (long) Math.abs(S - right) + t_RL + (long) (N + 1) * Math.abs(left - G) + N; long t3 = (long) Math.abs(S - left) + t_LR + t_RL + (long) (N + 1) * Math.abs(left - G) + N; long t4 = (long) Math.abs(S - right) + t_RL + t_LR + (long) (N + 1) * Math.abs(right - G) + N; long minTime = Math.min(Math.min(t1, t2), Math.min(t3, t4)); if (minTime <= T) { bw.write("Yes\n"); } else { bw.write("No\n"); } } bw.flush(); bw.close(); br.close(); } }五、复杂度分析
- 预处理:排序 + 前缀和计算 ,总 。
- 查询:每个查询计算4种路径时间, 处理,总 。
- 整体复杂度 ,适配 的数据规模。
- 1
信息
- ID
- 4733
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者