本篇隨筆講解演算法競賽中的點分治澱粉質演算法。
點分治是一種在樹上進行路徑靜態統計的演算法。
所謂樹上的靜態統計,並不是像樹剖一樣維護路徑最值、路徑和之類的統計。那樣的話,這個演算法的存在就沒什麼意義了。
來上道小題體會一下點分治的解決問題。
給定一棵有n個節點的帶邊權無根樹。求長度不超過k的路徑有多少條。
看懂了麼?就是這種有限制的路徑統計問題,非常適合用點分治解決。
點分治點分治,最終還是落回分治這個東西。分治是什麼?其原理就是把原問題拆解成幾個子問題,分別求解子問題之後再合併出原問題。那麼點分治其實就是對一棵樹進行拆開,分治。
那麼用上面的問題作為引入。
顯然,暴力的方式是列舉所有路徑,然後判斷是否合法。但是所有的路徑都可以分兩種情況:第一種,過根節點。第二種,不過根節點。
那麼我們把根節點斷掉,又形成了很多新子樹,對於這些子樹,仍然會對它們的路徑分這兩種情況......
可以看出,所有不過整棵樹根節點的路徑,必會在這種不斷拆解形成新子樹的過程中,過一遍根節點。
對其有感覺麼?這就是遞迴的過程麼。
於是點分治實現的大致思路就是:
1、任取乙個點作為無根樹根節點。
2、計算所有第一種路徑。
3、將當前根節點斷掉,遞迴計算下一層子樹。步驟同上。
點分治過程中,分治點的選擇至關重要。
乙個遞迴層的時間複雜度是\(o(n\log n)\)
極端情況下,樹會退化成鏈,那麼對於鏈,我們最不划算的選擇分治點方式是每次選擇鏈頭,那麼就會變成遞迴n次,總複雜度是\(o(n^2\log n)\)。
那麼我們每次選擇鏈的中心呢?
很簡單,遞迴變成了log次,總複雜度就是\(o(n\log^2 n)\)。
那麼,對於鏈是這樣選擇,對於樹也是這樣選擇。選」中點「當然是最划算的。那麼樹的「中點」是什麼呢?
即,樹的重心。
重心有乙個性質:那就是刪除重心後,樹被拆成若干個部分,這些部分中,不會有任何乙個部分超過總點數的二分之一。否則就不符合重心的性質。
所以,每次選擇重心作為分治點,就保證了整個點分治演算法的時間是\(o(n\log^2 n)\)
根據第二部分的思路概述,我們應該很容易用遞迴方式寫出其框架**:
void dfz(int x)//大多數人叫divide,我就喜歡這樣
}
其中的getroot函式,功能是找子樹重心。根據重心的定義及之前學過的求法,應該這麼寫:
void getroot(int x,int f)
mp[x]=max(mp[x],sum-size[y]);
if(mp[x]只有這兩個能算作模板化的東西。
然而點分治的精華,calc函式部分,需要按題目的意思來求。其功能就是維護題目想要維護的資訊。
這個就需要自己通過例題和練習細細體會。
點分治詳解
今天做了一道點分治的題目,所以就去網上學了一下。相信大家都聽說過 分治 吧,分治就是 分而治之 一般是把n分成2份,然後再對每乙份進行相同的操作,最後合併起來。而點分治,一般情況下是在一棵樹上面進行分治,和普通的分治大同小異。先看一道例題 題意 給定乙個有n個點 編號1,2,n 的樹,每條邊都有乙個...
點分治 模板 詳解
動態點分治學了以後會在後面 update 的啦 好久沒頹 blog 了 今天來寫一發 最近幾個月就學了這乙個東西啊 好了 進入正題 舌尖上的澱粉質 q1 點分治是什麼?a1 就是像分治一樣把樹上的點咔擦成幾個小樹,然後繼續咔擦下去處理問題啦 像你把西蘭花掰開一樣 q2 點分治能用來幹什麼?a2 據我...
點分治 動態點分治
實在拖得太久了。先扔掉資料 分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n 變成nlogn。兩個問題,如何區分和如何算答案。對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去...