題目的難度顏色使用 Luogu 上的分級,由簡單到困難分別為 🔴🟠🟡🟢🔵🟣⚫。
rating: 1305
Problem Statement
題意簡述
平面上有 N N N 棵聖誕樹,第 i i i 棵樹位於 ( X i , Y i ) (X_i, Y_i) ( X i , Y i ) 。
需依序處理 Q Q Q 個詢問:
Type 1 (1 i x y):將第 i i i 棵樹的座標變更為 ( x , y ) (x, y) ( x , y ) 。
Type 2 (2 L R x y):計算點 ( x , y ) (x, y) ( x , y ) 到第 L , L + 1 , … , R L, L+1, \ldots, R L , L + 1 , … , R 棵樹中,最遠 的曼哈頓距離 (Manhattan Distance)。
曼哈頓距離 (Manhattan Distance)
其中曼哈頓距離定義為 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ |x_1 - x_2| + |y_1 - y_2| ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ 。
Constraints
約束條件
1 ≤ N , Q ≤ 2 × 1 0 5 1 \le N, Q \le 2 \times 10^5 1 ≤ N , Q ≤ 2 × 1 0 5
− 1 0 9 ≤ X i , Y i , x , y ≤ 1 0 9 -10^9 \le X_i, Y_i, x, y \le 10^9 − 1 0 9 ≤ X i , Y i , x , y ≤ 1 0 9
思路:曼哈頓距離轉切比雪夫距離 + 線段樹
這是一道經典的 曼哈頓距離 (Manhattan Distance) 處理問題。題目要求在動態修改點座標的情況下,查詢一段區間內的點到給定點的最大曼哈頓距離。
核心轉換
曼哈頓距離 ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ |x_1 - x_2| + |y_1 - y_2| ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ 涉及絕對值,我們可以根據 x 1 − x 2 x_1 - x_2 x 1 − x 2 與 y 1 − y 2 y_1 - y_2 y 1 − y 2 的符號關係來拆解。
令 A = x 1 − x 2 A = x_1 - x_2 A = x 1 − x 2 ,B = y 1 − y 2 B = y_1 - y_2 B = y 1 − y 2 。
若 A , B A, B A , B 同號(同正或同負),則 ∣ A ∣ + ∣ B ∣ = ∣ A + B ∣ |A| + |B| = |A+B| ∣ A ∣ + ∣ B ∣ = ∣ A + B ∣ 。
若 A , B A, B A , B 異號,則 ∣ A ∣ + ∣ B ∣ = ∣ A − B ∣ |A| + |B| = |A-B| ∣ A ∣ + ∣ B ∣ = ∣ A − B ∣ 。
綜合這兩種情況,我們可以得到恆等式:∣ A ∣ + ∣ B ∣ = max ( ∣ A + B ∣ , ∣ A − B ∣ ) |A| + |B| = \max(|A+B|, |A-B|) ∣ A ∣ + ∣ B ∣ = max ( ∣ A + B ∣ , ∣ A − B ∣ ) 。
將 A , B A, B A , B 代回原式並整理:
∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ = max ( ∣ ( x 1 − x 2 ) + ( y 1 − y 2 ) ∣ , ∣ ( x 1 − x 2 ) − ( y 1 − y 2 ) ∣ ) = max ( ∣ ( x 1 + y 1 ) − ( x 2 + y 2 ) ∣ , ∣ ( x 1 − y 1 ) − ( x 2 − y 2 ) ∣ ) \begin{aligned}
|x_1 - x_2| + |y_1 - y_2| &= \max(|(x_1 - x_2) + (y_1 - y_2)|, |(x_1 - x_2) - (y_1 - y_2)|) \\
&= \max(|(x_1 + y_1) - (x_2 + y_2)|, |(x_1 - y_1) - (x_2 - y_2)|)
\end{aligned}
∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ = max ( ∣ ( x 1 − x 2 ) + ( y 1 − y 2 ) ∣ , ∣ ( x 1 − x 2 ) − ( y 1 − y 2 ) ∣ ) = max ( ∣ ( x 1 + y 1 ) − ( x 2 + y 2 ) ∣ , ∣ ( x 1 − y 1 ) − ( x 2 − y 2 ) ∣ )
事實上,這正好是兩點在座標轉換 ( u , v ) = ( x + y , x − y ) (u, v) = (x+y, x-y) ( u , v ) = ( x + y , x − y ) 後的 切比雪夫距離 (Chebyshev Distance) 。
為了方便,我們定義每個點 i i i 的兩個特徵值:
S i = X i + Y i S_i = X_i + Y_i S i = X i + Y i (Sum)
D i = X i − Y i D_i = X_i - Y_i D i = X i − Y i (Difference)
那麼點 ( X i , Y i ) (X_i, Y_i) ( X i , Y i ) 與查詢點 ( x , y ) (x, y) ( x , y ) 的曼哈頓距離可以表示為:
dist i = max ( ∣ S i − ( x + y ) ∣ , ∣ D i − ( x − y ) ∣ ) \text{dist}_i = \max(|S_i - (x+y)|, |D_i - (x-y)|)
dist i = max ( ∣ S i − ( x + y ) ∣ , ∣ D i − ( x − y ) ∣ )
去除絕對值
我們要查詢的是區間 [ L , R ] [L, R] [ L , R ] 內所有 i i i 的最大距離。對於固定的查詢點 ( x , y ) (x, y) ( x , y ) ,上式中的絕對值可以拆解為四種情況取最大值:
S i − ( x + y ) S_i - (x+y) S i − ( x + y )
( x + y ) − S i (x+y) - S_i ( x + y ) − S i
D i − ( x − y ) D_i - (x-y) D i − ( x − y )
( x − y ) − D i (x-y) - D_i ( x − y ) − D i
也就是說,對於區間查詢,我們只需要找到區間 [ L , R ] [L, R] [ L , R ] 內的:
max ( S i ) \max(S_i) max ( S i )
min ( S i ) \min(S_i) min ( S i )
max ( D i ) \max(D_i) max ( D i )
min ( D i ) \min(D_i) min ( D i )
只要維護這四個數值,答案即為:
max { max ( S i ) − ( x + y ) ( x + y ) − min ( S i ) max ( D i ) − ( x − y ) ( x − y ) − min ( D i ) \max \begin{cases}
\max(S_i) - (x+y) \\
(x+y) - \min(S_i) \\
\max(D_i) - (x-y) \\
(x-y) - \min(D_i)
\end{cases}
max ⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ max ( S i ) − ( x + y ) ( x + y ) − min ( S i ) max ( D i ) − ( x − y ) ( x − y ) − min ( D i )
資料結構選擇
由於需要支援 單點修改 (Type 1) 與 區間最值查詢 (Type 2),我們可以使用 線段樹 (Segment Tree) 。
具體來說,我們需要建立 4 棵線段樹(或是 1 棵維護 4 個值的線段樹):
seg1: 維護區間 S i S_i S i 的最大值
seg2: 維護區間 S i S_i S i 的最小值
seg3: 維護區間 D i D_i D i 的最大值
seg4: 維護區間 D i D_i D i 的最小值
使用 AtCoder Python Library (ACL) 的 segtree 可以很方便地實作。
op 函數分別設為 max 和 min。
e (單位元) 分別設為 float('-inf') 和 float('inf')。
複雜度分析
時間複雜度:O ( Q log N ) \mathcal{O}(Q \log N) O ( Q log N )
建樹:O ( N ) \mathcal{O}(N) O ( N )
每次更新 (Type 1):O ( log N ) \mathcal{O}(\log N) O ( log N )
每次查詢 (Type 2):O ( log N ) \mathcal{O}(\log N) O ( log 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 34 from atcoder.segtree import SegTreefmax = lambda a, b : a if a > b else b fmin = lambda a, b : a if a < b else b def solve (): n, q = map (int , input ().split()) P = [tuple (map (int , input ().split())) for _ in range (n)] assert len (P) == n seg1 = SegTree(fmax, float ('-inf' ), [x + y for x, y in P]) seg2 = SegTree(fmin, float ('inf' ), [x + y for x, y in P]) seg3 = SegTree(fmax, float ('-inf' ), [x - y for x, y in P]) seg4 = SegTree(fmin, float ('inf' ), [x - y for x, y in P]) for _ in range (q): op, *args = map (int , input ().split()) if op == 1 : i, x, y = args seg1.set (i - 1 , x + y) seg2.set (i - 1 , x + y) seg3.set (i - 1 , x - y) seg4.set (i - 1 , x - y) else : l, r, x, y = args max_s = seg1.prod(l - 1 , r) min_s = seg2.prod(l - 1 , r) max_d = seg3.prod(l - 1 , r) min_d = seg4.prod(l - 1 , r) print (max (max_s - (x + y), (x + y) - min_s, max_d - (x - y), (x - y) - min_d)) if __name__ == "__main__" : solve()
寫在最後
PROMPT