AtCoder 🟢 ABC435E Cover query
題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
🔗 ABC435E Cover query
rating: 1011
Problem Statement
題目簡述
有 個格子排成一列,初始全白。
依序進行 次操作,每次給定整數 ,將區間 內的所有格子塗黑(已黑者保持黑)。
每次操作後,請輸出當前仍為白色的格子數量。
Constraints
約束條件
- 所有輸入皆為整數。
思路:離散化 + Lazy Segment Tree
正難則反:轉換問題目標
隨著操作進行,白色部分會被切割得支離破碎,因此維護白色格子的數量較為困難。
相對地,塗黑操作是明確的區間覆蓋,故維護目前被塗黑的格子總數(記為 )會相對容易。
每次查詢的答案即為:
核心解法:離散化 + 線段樹
這是一道典型的區間覆蓋問題,但由於 高達 ,無法直接建立長度為 的陣列。
觀察到操作次數 僅 ,且塗色操作只與給定的端點有關,這提示我們使用 離散化 配合 線段樹(Segment Tree) 來解決。
為什麼可以維護區間?(離散化原理)
染黑操作的本質是區間聯集。顏色的變化(由白變黑)只會發生在查詢區間的邊界處。
我們將所有相關的座標點收集起來:對於每個查詢 ,將 和 加入集合。
為什麼要用左閉右開區間 ?
使用半開區間的主要好處是:區間邊界不會重疊,使得相鄰區間可以無縫拼接,且長度計算統一為 。
舉例說明:
假設有兩個操作:塗黑 和 (共享端點 )。
- 閉區間做法:收集端點 。
- 形成的區間段:、。
- 問題:格子 同時是前一段的右端點與後一段的左端點,在處理時容易重複計算或需要額外判斷。
- 半開區間做法:將 轉換為 , 轉換為 。
- 收集端點 。
- 形成的區間段:、、,長度分別為 。
- 每個區間段的邊界不重疊,長度即為端點差,且自動處理了重疊部分。
將收集到的座標排序去重後,得到序列 (共 個點)。
這些座標點將整個數線切分成了 個基本區間 (對應程式碼中 len(Xs)-1)。
對於任意一個基本區間,不可能有操作只操作區間的其中一部份。
因此,我們可以將每個區間 視為一個不可分割的單元(葉節點),其長度固定為 。
線段樹中維護了哪些資訊?
我們使用 Lazy Segment Tree 來維護離散化後的區間,目標是即時查詢「被染黑的總長度」,答案即為 。
節點狀態 = (b, w)
(b, w)| 欄位 | 意義 | 特性 |
|---|---|---|
b |
該區間內已被染黑的長度 | 動態更新 |
w |
該區間的總長度 | 建樹後固定 |
LazySegTree 介面定義(ACL)
使用 LazySegTree 需定義以下元素:
| 元素 | 型別 | 本題定義 | 說明 |
|---|---|---|---|
op(a, b) |
合併兩個子區間狀態 | ||
e |
狀態單位元素 | ||
mapping(f, s) |
if else | Tag 作用於狀態 | |
composition(f, g) |
Tag 合成 | ||
id |
Tag 單位元素(無操作) |
註:一旦染黑就不會變回白,重複染黑也不會有額外效果,故 Tag 合成使用 Bitwise OR 即可。
複雜度分析
- 時間複雜度:
- 離散化需排序座標,而座標的數量最多為 ,耗時 。
- 線段樹葉節點數最多 ,建樹 。
- 每次查詢進行區間更新,耗時 ,總共 。
- 空間複雜度:
- 需儲存離散化座標及線段樹結構。
Code
1 | from itertools import pairwise |
寫在最後
PROMPT
這裡什麼都沒有。
本部落格所有文章除特別聲明外,均採用CC BY-NC-SA 4.0 授權協議。轉載請註明來源 十六個天亮!
評論
WalineGiscus











