Description有 N N N 个按钮。第 i i i 个按钮 ( 1 ≤ i ≤ N ) (1 \le i \le N) ( 1 ≤ i ≤ N ) 在游戏开始后 T i T_i T i 秒出现在坐标 X i X_i X i 处。每个按钮在出现 D + 0.5 D + 0.5 D + 0 . 5 秒后消失。
玩家从坐标 0 0 0 开始,时间为 0 0 0 ,必须按下所有 N N N 按钮才能赢得游戏。按键的顺序不限。但是,在按下一个按钮后和按下另一个按钮前,玩家必须至少访问一次坐标 0 0 0 。违反此规则将被取消游戏资格。
AtCoder 女士可以在线路上以 1 1 1 的速度移动。她能赢得比赛吗?按下按钮所需的时间可以忽略不计。
解决 T E S T C A S E S \mathrm{TESTCASES} T E S T C A S E S 个测试案例。
n ≤ 5000 , ∑ n 2 ≤ 2.5 × 1 0 7 n\leq 5000,\sum n^2\leq 2.5\times 10^7 n ≤ 5 0 0 0 , ∑ n 2 ≤ 2 . 5 × 1 0 7 。
Solution首先可以对问题做个转化:第 i i i 个任务在 T i − X i T_i-X_i T i − X i 时刻出现,做任务需要 2 X i 2X_i 2 X i 的时间,并要求必须要在 T i + X i + D T_i+X_i+D T i + X i + D 时刻之前完成 任务。设 L i = T i − X i L_i=T_i-X_i L i = T i − X i ,R i = T i + X i + D R_i=T_i+X_i+D R i = T i + X i + D 。
先考虑没有 L L L 的限制的情况。
容易发现此时按照 R i R_i R i 排序一定更优,证明就考虑邻项交换,如果 R i > R i + 1 R_i>R_{i+1} R i > R i + 1 ,则完成 i − 1 i-1 i − 1 号任务的最晚时刻为 min ( R i − 2 X i , R i + 1 − 2 X i − 2 X i + 1 ) = min ( R i + 2 X i + 1 , R i + 1 ) − 2 X i − 2 X i + 1 \min(R_i-2X_i,R_{i+1}-2X_i-2X_{i+1})=\min(R_i+2X_{i+1},R_{i+1})-2X_i-2X_{i+1} min ( R i − 2 X i , R i + 1 − 2 X i − 2 X i + 1 ) = min ( R i + 2 X i + 1 , R i + 1 ) − 2 X i − 2 X i + 1 ,交换后为 min ( R i + 1 + 2 X i , R i ) − 2 X i − 2 X i + 1 \min(R_{i+1}+2X_i,R_i)-2X_i-2X_{i+1} min ( R i + 1 + 2 X i , R i ) − 2 X i − 2 X i + 1 ,显然交换后更优。
加上 L L L 的限制后就不能直接按照 R R R 排序了,因为可能出现 L 2 < L 1 < R 1 < R 2 L_2<L_1<R_1<R_2 L 2 < L 1 < R 1 < R 2 的情况,此时如果先做 1 1 1 任务需要先等待到 L 1 L_1 L 1 时刻,可能会有浪费,先做 2 2 2 再做 1 1 1 则会更优。
不过如果 L L L 和 R R R 分别有单调性的话原来的做法是没问题的。
注意到如果先做了 R R R 更大的任务 i i i ,则所有 R j < R i R_j<R_i R j < R i 的任务 j j j 就都已经激活,因为做 i i i 任务的最小结束时间为 L i + 2 X i = T i + X i ≥ T j + X j > L j L_i+2X_i=T_i+X_i\geq T_j+X_j>L_j L i + 2 X i = T i + X i ≥ T j + X j > L j 。此时可以把 L j L_j L j 看成 0 0 0 ,由于这些 R j < R i R_j<R_i R j < R i 的任务的 L L L 和 R R R 都满足单调不降的性质,所以先按照 R R R 的大小顺序做这些 R R R 小于 R i R_i R i 的任务一定更优。
具体来说,按照 R i R_i R i 排序后的可能操作顺序长这样:( 1 ) (1) ( 1 ) , ( 2 ) (2) ( 2 ) , ( 6 , 3 , 4 , 5 ) (6,3,4,5) ( 6 , 3 , 4 , 5 ) , ( 8 , 7 ) (8,7) ( 8 , 7 ) , ( 9 ) (9) ( 9 ) , ( 10 ) (10) ( 1 0 ) 。
设 f i f_i f i 表示做完前 i i i 个任务的最小时间,然后枚举下一个做的任务即可。
时间复杂度:O ( n 2 ) O(n^2) O ( n 2 ) 。
Code1 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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <bits/stdc++.h> using i64 = int64_t ;const int kMaxN = 5e3 + 5 ;const i64 kInf = 1e18 ;int n; i64 d, t[kMaxN], x[kMaxN], id[kMaxN], l[kMaxN], r[kMaxN]; i64 f[kMaxN], s[kMaxN], g[kMaxN][kMaxN];void getg () { for (int i = 1 ; i <= n; ++i) { i64 sum = 0 ; g[i][i - 1 ] = kInf; for (int j = i; j <= n; ++j) { sum += 2 * x[id[j]]; g[i][j] = std::min (g[i][j - 1 ], r[j] - sum); } } }void dickdreamer () { std::cin >> n >> d; for (int i = 1 ; i <= n; ++i) { std::cin >> t[i] >> x[i]; id[i] = i; } std::sort (id + 1 , id + 1 + n, [&] (int i, int j) { return t[i] + x[i] < t[j] + x[j]; }); for (int i = 1 ; i <= n; ++i) { l[i] = t[id[i]] - x[id[i]], r[i] = t[id[i]] + x[id[i]] + d; s[i] = s[i - 1 ] + 2 * x[id[i]]; } getg (); f[0 ] = 0 , std::fill_n (f + 1 , n, kInf); for (int i = 0 ; i < n; ++i) { for (int j = i + 1 ; j <= n; ++j) { i64 now = std::max (f[i], l[j]) + 2 * x[id[j]]; if (now <= r[j] && now <= g[i + 1 ][j - 1 ]) f[j] = std::min (f[j], now + s[j - 1 ] - s[i]); } } std::cout << (f[n] != kInf ? "Yes\n" : "No\n" ); }int32_t main () {#ifdef ORZXKR freopen ("in.txt" , "r" , stdin); freopen ("out.txt" , "w" , stdout);#endif std::ios::sync_with_stdio (0 ), std::cin.tie (0 ), std::cout.tie (0 ); int T = 1 ; std::cin >> T; while (T--) dickdreamer (); return 0 ; }