淺談樹分治

前言

內容大概有

  • 點分治

點分治

兩年前好像寫過一篇關於點分治的文章(但是菜得很):https://blog.csdn.net/qq_38944163/article/details/81544134

用於解決某類樹上路徑的問題的高效算法

算法核心思想: 每次欽定一個點作爲根,統計經過這個點的路徑的貢獻(兩端都在子樹內),然後這個點各個兒子子樹之前不再有貢獻,一路分治下去即可(兒子樹的互相獨立)。

在樹上欽定的點要保證刪掉這個點後最大的連通塊節點個數最小,這個點就是樹的重心。

找重心:
  • 首先隨便找一個點爲根dfs,統計出每個子樹的大小
  • 假設把uu刪掉後最大的連通塊節點個數是 max{size[v1],size[v2],...size[vk],nsize[u]}max{\{size[v_1], size[v_2],...size[v_k],n-size[u] }\},v1...kv_{1...k}uu的兒子節點
  • 最後一個for找到一個u使得連通塊節點個數最小就好了(max{size[v1],size[v2],....,size[vk]}max {\{size[v_1],size[v_2],....,size[v_k]}\}可以在dfs的時候就求出來)
  • 然後就沒了
    這麼做一次的大小是O()O(當前樹的總節點個數)
複雜度證明

一定可以存在一個點使得它的每個兒子的子樹大小都不大於 2\frac{總節點}{2}
可以用主定理證明
T(n)=2T(n/2)+n         =2(2T(n/4)+n/2)+nT(n) = 2*T(n / 2) + n \\ \ \ \ \ \ \ \ \ \ = 2*(2*T(n/4)+n/2)+n
         =4T(n/4)+2n\ \ \ \ \ \ \ \ \ = 4*T(n/4) +2*n
         =4(2T(n/8)+n/4)+2n\ \ \ \ \ \ \ \ \ = 4*(2*T(n/8)+n/4) +2*n
         =8T(n/8)+3n\ \ \ \ \ \ \ \ \ = 8*T(n/8) +3*n
         =nlog2n\ \ \ \ \ \ \ \ \ = nlog_2n

也可以簡單考慮,每個兒子子樹大小都不大於2\frac{總節點}{2},所以每次分治下去,當前樹的節點數/2,最多分治log層,每層都是n個,所以時間複雜度O(nlog2n)O(nlog_2n)

算法實現
int solve(int u) { //處理以u爲根的子樹
	dfs(u);
	for 找重心 u
	calc(u);//處理通過u的路徑
	vis[u] = 1;
	for v : son[u]
		if(!vis[v]) solve(v);
}
例題

luogu P4149 [IOI2011]Race

套路題
看代碼能懂 (以前的代碼,很醜)

評測記錄 code

luogu P4178 Tree
和第一題一樣,唯一的不同就是計算的是<=的,算的時候用樹狀數組維護前綴和就好了
評測記錄

SP1825 FTOUR2 - Free tour II
上一題改成維護最大值就好了
評測記錄

未完待續……
To be continue……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章