題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
Problem Statement
題目簡述
河岸上的格子編號為 0 0 0 到 N N N ,起點在 0 0 0 ,且 A 0 = 0 A_0=0 A 0 = 0 。
每次若目前在格子 i i i ,下一步只能跳到區間 [ i + L , i + R ] [i+L, i+R] [ i + L , i + R ] 內的任一格,並獲得落點格子的冰凍指數。
只要下一步能跳到編號大於 N N N 的位置,就算成功到達對岸。
請求出在最佳走法下,能取得的最大冰凍指數總和。
Constraints
約束條件
1 ≤ N ≤ 2 × 1 0 5 1 \le N \le 2\times 10^5 1 ≤ N ≤ 2 × 1 0 5
1 ≤ L ≤ R ≤ N 1 \le L \le R \le N 1 ≤ L ≤ R ≤ N
− 1 0 3 ≤ A i ≤ 1 0 3 -10^3 \le A_i \le 10^3 − 1 0 3 ≤ A i ≤ 1 0 3
A 0 = 0 A_0 = 0 A 0 = 0
保證最終答案不超過 2 31 − 1 2^{31}-1 2 3 1 − 1
思路:單調佇列優化DP
1. 狀態與轉移:每一步只看「可轉移來源」的最大值
把「走到某格子時的最佳總分」當成狀態值。
令 f [ i ] f[i] f [ i ] 表示「最後停在格子 i i i 時,可以取得的最大冰凍指數總和 」。
若最後一步落在 i i i ,上一個落點必須能一步跳到 i i i 。由於每次跳躍長度介於 L L L 到 R R R ,反推上一格的位置範圍就是:
i − R ≤ j ≤ i − L i-R \le j \le i-L
i − R ≤ j ≤ i − L
因此轉移為:
f [ i ] = A i + max i − R ≤ j ≤ i − L f [ j ] f[i] = A_i + \max_{i-R \le j \le i-L} f[j]
f [ i ] = A i + i − R ≤ j ≤ i − L max f [ j ]
起點在 0 0 0 且 A 0 = 0 A_0=0 A 0 = 0 ,所以:
f [ 0 ] = 0 f[0] = 0
f [ 0 ] = 0
有些格子根本到不了,對應的狀態值應視為 − ∞ -\infty − ∞ ,避免被拿來轉移造成錯誤。
2. 為什麼需要優化:樸素轉移太慢
若每個位置都暴力枚舉所有合法的上一格,單次要掃描大約 R − L + 1 R-L+1 R − L + 1 個候選,總複雜度為:
O ( N ( R − L + 1 ) ) \mathcal{O}(N(R-L+1))
O ( N ( R − L + 1 ) )
在 N ≤ 2 × 1 0 5 N \le 2\times 10^5 N ≤ 2 × 1 0 5 時會超時。
3. 單調佇列的切入點:滑動窗口區間最大值
觀察轉移式其實只是在問:
max i − R ≤ j ≤ i − L f [ j ] \max_{i-R \le j \le i-L} f[j]
i − R ≤ j ≤ i − L max f [ j ]
也就是「隨著 i i i 右移的窗口 [ i − R , i − L ] [i-R, i-L] [ i − R , i − L ] 中,狀態值的最大值」。這正是單調佇列能做到攤還 O ( 1 ) \mathcal{O}(1) O ( 1 ) 查詢的情境。
做法是用雙端佇列維護一串「可能成為最佳上一格的位置」,並維持兩個不變量:
佇列中的下標都落在當前合法窗口內(過期就從前端丟掉)。
佇列對應的狀態值單調遞減(新加入的若更好,就把後端較差的候選移除)。
如此一來,佇列前端永遠是窗口內狀態值最大的候選,因此每個 i i i 都能在常數時間拿到最佳轉移來源。
把「轉移來源區間的最大值」改用單調佇列維護後,每個位置只需要常數攤還時間就能完成轉移,整題變成線性時間。
4. 終點判定:哪些格子可能是最後落點?
題目判定成功的條件是「存在一步能跳到編號大於 N N N 的位置」。
若目前停在格子 i i i ,只要:
i + R > N i + R > N
i + R > N
就能在下一步跳出河岸。
等價於:
i ≥ N − R + 1 i \ge N - R + 1
i ≥ N − R + 1
所以答案是區間 [ N − R + 1 , N ] [N-R+1, N] [ N − R + 1 , N ] 中所有可達狀態值的最大者。
複雜度分析
時間複雜度:O ( N ) \mathcal{O}(N) O ( N )
空間複雜度:O ( N ) \mathcal{O}(N) O ( N )
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 from collections import dequedef solve (): n, L, R = map (int , input ().split()) A = list (map (int , input ().split())) assert len (A) == n + 1 f = [0 ] + [float ("-inf" )] * n q = deque() for i, x in enumerate (A): if i - L >= 0 : while q and f[q[-1 ]] <= f[i - L]: q.pop() q.append(i - L) while q and q[0 ] < i - R: q.popleft() if q: f[i] = max (f[i], f[q[0 ]] + x) print (max (f[n - R + 1 :])) if __name__ == "__main__" : solve()