AtCoder 🟡 ABC437E Sort Arrays
題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
🔗 ABC437E Sort Arrays
rating: 1245
Problem Statement
題意簡述
有 個序列 ,定義如下:
- 是一個空序列
()。 - 對於 ,序列 是將整數 追加到序列 的末尾 ()。
- 例如:若 且 ,則 。
請將這 個序列 () 依照字典序由小到大排序後,給出由 的下標 構成的一個排列 。
- 如果兩個序列內容完全相同 (),則索引較小的排在前面(即若 ,則 排在 之前)。
字典序 (Lexicographical Order) 定義
序列 字典序小於 (),若滿足以下任一條件:
- 是 的前綴,且 的長度小於 (例如
(1, 2)<(1, 2, 3))。 - 在兩者第一個數值不同的位置 , (例如
(1, 2, 5)<(1, 4, 1),因為在第 2 位 )。
Constraints
約束條件
Example
Input 1
1 | 4 |
Output 1
1 | 2 4 3 1 |
解釋
- (接在 後)
- (接在 後)
- (接在 後)
- (接在 後)
排序後:
- (與 相等,索引較大排後)
- ( 開頭,長度較長)
故順序為 。
思路:在 Trie 上進行 DFS 遍歷
Trie 結構模擬
這道題目的序列生成過程本質上就是在構建一棵 Trie (字典樹)。
- 是 Trie 的根節點。
- 每個操作 代表從 Trie 節點 延伸出一條邊,邊權為 ,到達一個新的狀態(節點)。
- 由於題目保證 ,這棵樹是隨著輸入依序建立的,不會有環。
直觀理解:樹結構
這個生成過程實際上構成了一棵以 為根的樹(Trie):
- 每個索引 代表樹上的一個節點。
- 是由 延伸而來,這相當於樹上從父節點 到子節點 有一條權重為 的邊。
- 序列 的內容,就是從根節點 走到節點 的路徑上所有邊權組成的序列。
比較兩個序列 的字典序:
- 前綴關係:若節點 是節點 的祖先,則 是 的前綴,故 。
- 分歧點:若兩者在某個節點分岔,則比較分岔邊的權重 大小。
處理相同序列
題目要求當序列相同時,索引小的排在前面。
在我們的 Trie 結構中,每個節點代表一個獨一無二的序列內容。
我們可以讓每個 Trie 節點維護一個列表 nodes,儲存所有「終止於此」的序列編號 。
由於我們是按照 的順序讀入並建立 Trie,對於同一個節點,其 nodes 列表中的編號自然是遞增的。
DFS 遍歷求字典序
字典序的排序對應到 Trie 上,就是 前序遍歷 (Pre-order Traversal) 的過程,但在訪問子節點時,必須按照邊權 () 由小到大的順序進行。
具體流程如下:
- 建樹:讀取輸入,利用
mp陣列紀錄每個編號 對應到的 Trie 節點。若該節點不存在則建立。將 加入該節點的nodes列表。 - DFS:從根節點開始遍歷。
- 當到達節點 時,首先輸出
u.nodes中的所有編號。這是因為 是其所有子樹中序列的前綴,根據字典序定義,前綴必定排在前面。 - 將 的所有子邊按照權重 排序。
- 依序遞迴訪問子節點。
- 當到達節點 時,首先輸出
實作細節
- 由於 高達 ,Python 的預設遞迴深度可能不足,需使用
sys.setrecursionlimit或改寫為迭代形式 (Iterative DFS)。 - 參考程式碼中使用了迭代方式(Stack)來模擬 DFS,避免遞迴過深的問題。
- 使用
defaultdict來動態建立 Trie 的邊,節省空間。
複雜度分析
- 時間複雜度:
- 建立鄰接表(樹結構)需要 。
- 對於每個節點 ,若其有 個子節點,我們需要對這些子節點進行排序,耗時 。
- 總排序時間為 。由於 ,在最壞情況下(星狀圖),這會達到 。
- DFS 遍歷每個節點一次,耗時 。
- 空間複雜度:
- 需要儲存鄰接表( 個節點, 條邊),空間為 。
- DFS 的遞迴深度或迭代堆疊在最壞情況下(鏈狀圖)為 。
Code
1 | from collections import defaultdict |
寫在最後
PROMPT
這裡什麼都沒有。
本部落格所有文章除特別聲明外,均採用CC BY-NC-SA 4.0 授權協議。轉載請註明來源 十六個天亮!
評論
WalineGiscus









