前言
內容大概有
- 點分治
點分治
兩年前好像寫過一篇關於點分治的文章(但是菜得很):https://blog.csdn.net/qq_38944163/article/details/81544134
用於解決某類樹上路徑的問題的高效算法
算法核心思想: 每次欽定一個點作爲根,統計經過這個點的路徑的貢獻(兩端都在子樹內),然後這個點各個兒子子樹之前不再有貢獻,一路分治下去即可(兒子樹的互相獨立)。
在樹上欽定的點要保證刪掉這個點後最大的連通塊節點個數最小,這個點就是樹的重心。
找重心:
- 首先隨便找一個點爲根dfs,統計出每個子樹的大小
- 假設把刪掉後最大的連通塊節點個數是 ,是的兒子節點
- 最後一個for找到一個u使得連通塊節點個數最小就好了(可以在dfs的時候就求出來)
- 然後就沒了
這麼做一次的大小是
複雜度證明
一定可以存在一個點使得它的每個兒子的子樹大小都不大於
可以用主定理證明
也可以簡單考慮,每個兒子子樹大小都不大於,所以每次分治下去,當前樹的節點數/2,最多分治log層,每層都是n個,所以時間複雜度
算法實現
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 P4178 Tree
和第一題一樣,唯一的不同就是計算的是<=的,算的時候用樹狀數組維護前綴和就好了
評測記錄
SP1825 FTOUR2 - Free tour II
上一題改成維護最大值就好了
評測記錄
未完待續……
To be continue……