1樓:
對於樹鏈剖分,常見的做法是按照輕重鏈進行剖分
然後用一棵全域性線段樹維護整棵樹按dfs序構成的序列
這種寫法的優勢有:實現方便、支援可持久化、支援子樹操作
對於這種做法,複雜度確實是 的,考慮一棵滿二叉樹即可
但是,如果只需要支援鏈上操作,那麼可以對每條實鏈都用一棵線段樹進行維護
在這種情況下,是否存在著比輕重鏈剖分更優的剖分方法呢?
在這種情況下,是否存在著比 更優的複雜度呢?
樹上一點到根的路徑會經過若干條實鏈,我們分別計算每條實鏈的貢獻
如果一條實鏈從頭到尾都被走過了,那麼它的貢獻是 1
如果一條實鏈只被走過了一部分,那麼它的貢獻是線段樹的深度 ,其中 k 為實鏈的長度
注意這個複雜度只是上界(不然也不會有全域性平衡二叉樹),但已經足夠精細了
我們可以用動態規劃來解這個問題
令dp[u]表示以u為根的子樹中,樹上所有點到根的路徑的複雜度的最大值
乙個根節點u會連出一條實鏈,而這條實鏈上的每個點又會連出若干條虛鏈
令len為實鏈的長度
令w_1, w_2..為實鏈中最深的節點連出的虛子節點,且dp[w_1] >= dp[w_2] >= ..
令v_1, v_2..為實鏈中的其他節點連出的虛子節點,且dp[v_1] >= dp[v_2] >= ..
則dp[u] = max(dp[w_1] + 1, dp[v_1] + lg[len])
這裡lg[len]的值是,之後的分析中也是如此
如果乙個節點v_1不存在,那麼它的dp值就是 0 ,之後的分析中也是如此
我們可以用動態規劃來解這個問題
令dp[u]表示以u為根的子樹中,最優的剖分方案的最壞複雜度
令dp[u][len]表示以u為根的子樹中,實鏈長度為len時,最優的剖分方案的最壞複雜度
令u.ch_1, u.ch_2..為u的子節點,且dp[u.ch_1] >= dp[u.ch_2] >= ..
那麼顯然有
dp[u] = min(dp[u][1], dp[u][2], ..)
dp[u][1] = dp[u.ch_1] + 1,對應於實鏈的長度為 1 時的最優剖分方案
dp[u][2] = max(dp[u.ch_1.ch_1] + 1, dp[u.
ch_2] + 2, dp[u.ch_1.ch_2] + 2),對應於實鏈的長度為 2 時的最優剖分方案
dp[u][3] = max(dp[u.ch_1.ch_1.
ch_1] + 1, max(dp[u.ch_2] , dp[u.ch_1.
ch_2] , dp[u.ch_1.ch_1.
ch2]) + 3),對應於實鏈的長度為 3 時的最優剖分方案
dp[u][k] = max(dp[u(.ch_1)^k] + 1, max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k]),對應於實鏈的長度為 k 時的最優剖分方案
也就是說,每次要增長實鏈的時候,都會選擇dp值最大的子節點,這本質上是乙個貪心演算法
觀察dp[u][k] = max(dp[u(.ch_1)^k] + 1, max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k])
我們發現隨著k的增加,左邊dp[u(.ch_1)^k] + 1不變或者減一,因為性質dp[u.ch_1] <= dp[u] <= dp[u.ch_1] + 1
我們發現隨著k的增加,右邊max(dp[u.ch_2], dp[u.ch_1.
ch_2], dp[u.ch_1.ch_1.
ch2], ..) + lg[k])不變或者增加
那麼dp[u][k]會在什麼時候取到最小值呢,那必然是在兩個單調函式相交的時候
dp[u(.ch_1)^(k - 1)] + 1 >= max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k - 1]
dp[u(.ch_1)^(k + 0)] + 1 >= max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k + 0]
dp[u(.ch_1)^(k + 1)] + 1 < max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k + 1]
dp[u(.ch_1)^(k + 2)] + 1 < max(dp[u.ch_2], dp[u.
ch_1.ch_2], dp[u.ch_1.
ch_1.ch2], ..) + lg[k + 2]
最終dp[u] = dp[u(.ch_1)^k] + 1
所以我們並不需要計算每乙個dp[u][k],要是要去找到單調函式相交時對應的那個k
我們令tail[u]表示以u為根的子樹的最優剖分中u所在的實鏈中深度最大的節點,即u(.ch_1)^(k - 1)
我們令heap[u]維護max(dp[u.ch_2], dp[u.ch_1.ch_2], dp[u.ch_1.ch_1.ch2], ..)
我們令deep[u]表示節點u在整棵樹中的的深度
那麼有dp[u] = dp[tail[u].ch_1] + 1
那麼有k = deep[u] - deep[tail[u]] + 1
那麼有dp[tail[u].ch_1] + 1 >= heap[u] + lg[deep[u] - deep[tail[u]] + 1]
我們從dp[u.ch_1], tail[u.ch_1], heap[u.
ch_1]著手計算dp[u], tail[u], heap[u],因為tail[u]必然是tail[u.ch_1]本身或其祖先
我們令tail[u] = tail[u.ch_1], heap[u] = heap[u.ch_1],然後把dp[u.ch_2]放到heap[u]裡
如果有dp[tail[u].ch_1] + 1 >= heap[u] + lg[deep[u] - deep[tail[u]] + 1],那麼就得到了正確的值
如果有dp[tail[u].ch_1] + 1 < heap[u] + lg[deep[u] - deep[tail[u]] + 1],那麼正確的值在tail[u]的祖先那裡
我們把dp[tail[u].ch_2]取出heap[u],然後令tail[u] = tail[u].father,迴圈往復,直到得到正確的值,最後算出dp[u]
如果用單調佇列來維護heap[u],時間複雜度為 O(n),非常高效
如果用堆來維護heap[u],時間複雜度為 O(nlgn),也可以接受
對於這個問題,我暫時還沒有得到明確的結果
但是對於很多自稱能卡樹剖的資料(比如OI wiki上的)表現良好
2樓:immortalCO
像這樣根號條根號的鏈構成乙個完全二叉樹,就能卡到 log(sqrt(n))^2=log(n)^2 了(每個點往上有 log 條重鏈,每條重鏈至少 sqrt(n) 長)。(專業卡樹剖)
這樣的資料,不在建線段樹上做文章的演算法都能卡掉。
老闆會反感自己的員工搞副業嗎?
半仙不抽菸 先不說反感,但是內心肯定是不支援的。首先,我們自己作為打工人肯定會覺得,你的工資只買了我日常的8個小時,我剩下的時間如何安排,作為老闆無權過問。這沒錯。但是作為老闆來說呢,你在8小時之外還在做別的副業,是不是就可以說明你花在工作上的時間沒那麼多了。或者換句話說,你會不會在原本該工作的8小...
為什麼自己種的樹,自己不能砍?這是真的嗎?有法律依據嗎? ?
濋薰 不是不能砍,是需要相關手序才能砍。自家的樹就算沒手序砍了,一般人也不會管,就怕被人舉報 農村居民採伐自留地和房前屋後個人所有的零星林木,不需要申辦林木採伐許可證。1 依據 森林法 第三十二條 採伐林木必須申請採伐許可證,按許可證的規定進行採伐 農村居民採伐自留地和房前屋後個人所有的零星林木除外...
舍友天天討論手機, 搞的好像自己很懂似的!?
王二丫 所以,你根本用不著有什麼不舒服的心情。如果有,那就是自己給自己找不愉快。你如果心裡不爽,就盡量不和他產生交流就可以了,做自己想做的事就好。如果沒有辦法避免和他交流,那就戴上面具對待他吧!最後,我想說一句,當你覺得別人的行為不好的時候,說明自己也有行為不好的地方。沒有乙個人是完美的,不是嗎?希...