樹上點分治學習筆記

2021-09-25 13:53:55 字數 2857 閱讀 2239

樹上點分治,用來處理這樣一些問題,即給定一棵樹,求出和樹上所有點對之間距離相關的某個答案,如求樹上兩點之間距離等於k的點對個數(cf161d,板子題)。

如果了解lca,可能會想到利用lca求出所有點對間的距離,然後列舉點對統計答案,複雜度大概是o(n2logn)。不過看一眼題,n最大為5e5,即使時間給了三秒,也應該會tle。

點分治的思想大概 是對於給定的樹,通過選取其中的特殊點(後面講),將其拆為不同子樹,並對子樹進行取點、拆分操作,然後分別對每棵子樹計算答案,最後統計結果。

簡單解釋一下畢竟這演算法我也不是很熟 ,考慮一棵樹上滿足條件的路徑可能由以下情況組成:1,在樹的某棵子樹上取某些邊;2,跨過樹根root,在兩邊的子樹中取邊組成。

不過再細想一下,對於情況1的那棵子樹,再把它看作幾棵子樹,那麼成路徑的方式其實和情況2一樣,即其路徑是兩個「小子樹」上選取邊跨過這棵「大子樹」的根組成的。

這樣基本思路可能 就出來了,分出子樹後考慮子樹就行了。

找根不能隨便找,因為要顯得演算法厲害 節省遍歷的時間,所以要找的點就應該滿足「以這個點作為樹根,其最大的子樹要盡量小」這個條件。這個點就叫重心。

對著**講:

const

int maxn=

50000+5

;const

int inf=

1e9;

int n,k,p,cntn;

//cntn可以理解為當前子樹要考慮的點的總個數,因為題中要求統計點對個數,沒問具體是哪些點對,這樣寫著方便一點

int h[maxn]

,w[maxn*2]

,v[maxn*2]

,nxt[maxn*2]

;//鄰接表**好

ll ans;

//最終答案

int mxsont[maxn]

,sz[maxn]

,focus,sn;

//mxsont[i]記錄以i為根的最大子樹的大小,sz[i]表示i的子樹大小,focus記錄當前重心,sn是當前考慮的「大子樹」的大小

bool deleted[maxn]

;//計算完乙個點的子樹,就把這個點記錄為刪去

ll dis[maxn]

;//dis[i]記錄i到當前子樹樹根的距離

把要定義的交代清楚,下面就是找重心的過程:

void

getfocus

(int x,

int fa)if(

(sn-sz[x]

)> mxsont[x]

)mxsont[x]

=sn-sz[x]

;//???

if(mxsont[x]

< mxsont[focus]

)focus = x;

}

解釋下問號那一句,畫個圖:

這裡假如我們遞迴到紅色節點x,計算完其子樹大小,後來發現它的「頭上」還有一部分,如果要把x當作樹根,那麼上面那一部分就應該是它的子樹,所以也需要比較一下,更新x的最大子樹大小。

好了到這裡我們就退賽了 找到了重心。

dfs**好

void

getdis

(int x,

int fa,

int d)

}

別忘了cntn是當前子樹節點個數

int

lookforl

(int l,

int check)

//找可能小於check的最小數

else

}return findl;

}int

lookforr

(int l,

int check)

//找可能小於check的最大數

else

}return findr;

}int

calansx

(int x,

int fad)

//統計x為根的子樹上的答案,fad可以理解為x到x的父親的距離,後面會講

++l;

}return ansx;

}

這裡用到了乙個二分,初次看比較難以理解,不過目的是很明確的,畢竟你要是枚舉子樹上的所有點統計答案也是可以試試的 還是會tle

void

dfsansn

(int x)

}

這裡就要講一講問號的兩句了,看圖

在對x統計答案的時候,對x下面的點a,b,計算dis+dis來統計答案是不合法的,因為我們重複計算了x.to到x之間的距離,所以當我們計算完x為根時子樹答案後,減去以to為根的子樹的非法路徑統計出的答案,而這些路徑之所以非法是因為計算兩點間距離時是加了to和x之間的距離的,學長說這是容斥原理但是他沒講清楚 我沒學過(還是菜啊)

到這裡,這題就已經a了

int

main()

sn=n;

mxsont[0]

=inf;

getfocus(1

,0);

dfsansn

(focus)

; cout<

"\n"

;return0;

}

當然還差這些神秘**。

終於碼完了,畢竟是初學,某些地方的解釋可能不是很嚴謹,歡迎指正。

另附上其他題目:洛谷p3806

poj1741

樹上點分治入門

分治就是分而治之。額 好像這話已經被說爛了。學樹分治之前我們先來回顧下線性分治。一般的線性分治都是將大的區間分成多個小的區間 一般是兩個 然後將已處理好的小區間合併從而得到了這個大區間的結果,為了使分的次數盡可能少,應每次使分的小區間的大小盡可能平均,這樣分的次數就會是log級別的。好,現在我們要分...

分治思想及樹上點分治

分治思想在oi中是一種常見的思想。分治的基本思想是將乙個規模為n的問題分解為k個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程式演算法,簡單問題可用二分法完成。當我們求解某些問題時,由於這些問題要處理的資料相當多,或求解過程相當複雜,使...

分治學習筆記

分治,即分而治之,將大問題分解為小問題,分別求解,最後合併結果。許多演算法都是建立在分治的基礎上的,比如說快速排序,歸併排序等 例題1 南蠻圖騰 不難發現每個圖案是由許多這個圖案組成的 然後就可以用分治來遞迴解決本題,別忘了處理空格喲 include using namespace std int ...