內容 :
Background
某 M 正與學姊討論蘿莉控追小海女的故事。
某 M 喃喃自語 …
『用了暴力法來完成的話,就能在調整 O(1) - O(E[x]) 完成。』
『如果用一個最常見的樹鏈剖分來完成也不過是 O(log n) - O(log^2 n)』
『在一般情況下,詢問次數肯定比調整次數來得高,想一想常數還得加上去,完全贏不了啊。』
學姊在一旁說到
『那如果是單純的二元樹,有比較好做嗎?說不定有更簡化的算法來完成?』
『說不定那個 … bitvertor 還是 bitset 能完成呢。』
『這個想法難不成是找到 lowbit 嗎?我沒有暫時想法。』某 M 這麼回答道。
『也許吧 … 我也不確定』學姊簇著眉。
某 M 發了瘋似的想了一個算法
『如果我們將其轉換成 k-ary search tree,也就是說需要將節點編號重新編成有大小關係,然後維護一個 BST 來查找 lower_bound 的結果,這麼一來就是 O(k log n) - O(log n) 了!』
『啊啊啊,這個方法不可行,』
學姊將鄙視般的眼神投向某 M。看來這場病還要持續很久。
『將題目弄成找樹前綴和好了,既然暴力法有期望值的剪枝,讓它剪不了不就好了!』
『你覺得不會被其他解法坑殺嗎 …』學姊如此擔憂地表示到。
『沒關係,吾等 M 可不是說假的』
Problem
給定一棵樹,樹根 root 編號 0,每個點一開始的權重為 0。
操作 x k: 將節點 x 的權重增加 k,請輸出從 x 到 root 的權重和。
輸入說明 :
輸入有多組測資,每組測資第一行會有一個整數 N (N < 32767),表示這棵樹有多少個節點。
接下來會有 N - 1 行,每一行上會有兩個整數 u, v (0 <= u, v < N) 表示 u, v 之間有一條邊。
接下來會有一行上有一個整數 Q (Q < 32767),表示接下來有 M 組詢問。
輸出說明 :
對於每個詢問結果輸出一行,請參照範例輸出的說明。
每組測資後空一行,保證總和可以在 signed 32 bits 內。
範例輸入 :
|
|
範例輸出 :
|
|
Solution 1
先將一棵樹壓平,壓平的方式為採用前序走訪。壓樹的另一個思考方式就是換成括弧表示法。(A (B) (C(D)(E)))
類似的情況。壓樹之後,每個節點對應一個左右括弧位置,當我們增加節點 v 權重時,相當於將所有區間的內數字加上 k (子樹的所有節點到根的花費)。
因此我們的問題變成
- 操作
[l, r]
:將區間內的所有數字加上 k - 詢問
x
:詢問位置 x 元素的值。
下面使用 Binary indexed tree 示範區間調整,單點詢問。
對於 ADD [l, r] k
時,相當於 BIT[l] += k, BIT[r+1] -= k
,單點詢問相當於找前綴和 [1, x]
的結果。
每個操作時間複雜度 O(log n)
。
|
|
Solution 2
這個解法直接使用樹鏈剖分,樹鏈剖分將子樹節點數量最大的當作重邊,其餘皆為輕邊。將以重邊相連的所有節點,看似一個鏈狀,當作一個 1D Array 看待。
保證每個點爬到 root,只會經過 log n 的輕邊,而中間鏈狀處理,可以使用 segment tree 等之類的樹來壓縮處理時間。
下面為樹鏈剖分的解法,詢問效率 O(log^2 n)
,調整 O(log n)
。
|
|