POJ 1741 點分治詳解

2021-08-07 17:52:57 字數 1451 閱讀 8825

題意:給出乙個n(<=1e4)個點的樹,每條邊有權,求樹上長度小於等於k的路徑條數。(u,v)和(v,u)算兩條。

點分治:顧名思義,點分治就是對點進行分治,一般用於路經統計問題。對每個點而言,一條路徑要麼經過他,要麼不經過他,這就是分,即分成路徑經過此點和不經過此點。基本思想是:當經過某點的路徑可以比較方便快速統計的情況下,通過對點進行分治,把樹的規模不斷變小。

考慮這樣做的好處:把樹看作無根樹,除了葉子結點外,去掉任意乙個點,都可以把一棵樹**成若干規模更小的樹。然後先統計好經過此點的路徑,然後只需要統計不經過此點的路徑,那麼就等價於把這個點刪掉,所以只要複雜度合理,點分治是普遍適用的。

關於點的選取:考慮最壞情況:一條鏈,如果順序選取,可能會出現每次都選了鏈的端點,那麼點分治本來是想通過對點進行分治,把一棵大樹,分解成幾棵小樹,分而治之。那麼如果隨意選點,很可能對點分治之後,分解出的樹規模和剛剛的差不多,導致效率極低。

樹的重心:一棵無根樹,對於某個點,把他去掉之後,原樹會**成幾棵小樹,每個小樹有若干個節點。樹的重心就是乙個點,去掉他之後,裂開後得到的最大的小樹規模最小,顯然,點分治中,對樹的重心進行分治是最好的。他可以穩定的把樹的規模不斷變小。

一般套路:對整個樹(聯通快),求出乙個重心,對重心這個點進行分治,先統計出經過這個點的路徑。然後把這個點刪掉。把整個樹分解成小樹(聯通快)。對每個小樹(聯通快)繼續求重心,繼續進行分治。

題解:考慮某個點x,經過x且長度小於等於k的路徑個數可以很方便求出:把樹變成x為根的樹,對每個點求出x到他的距離,然後雙指標進行組合。但是有乙個後果:某個經過x的路徑可以分成兩段,這兩段必須分布在兩個子樹上,如果在乙個子樹上,他們的lca不等於x,也就是不會經過x,上面統計完成之後,還要對每個子樹進行去重,文末的**更容易理解。想清楚經過某點的路徑如何統計之後,那麼就可以按照上面點分治的套路搞了。

個人感覺點分治就是乙個板子,求重心等等操作要用到dfs,都是不變的東西。每個題的差異就在於 對經過某點的路徑個數統計方法不一樣。

code:

#include#include#includeusing namespace std;

const int max = 1e4+100;

const int inf = 0x3f3f3f3f;

int first [max*2];

int des[max*2];

int len[max*2];

int nxt[max*2];

int n,k,tot;

int a[max];

int sum[max];

int dp[max];

int dis[max];

int num,ans;

bool vis[max];

int sum,min,minid;

void init()

inline void add(int x,int y,int z)

void input(){

for (int i=1;i

POJ1741 點分治模板

傳送門 求樹上兩點間路徑長度小於k的點對個數 參考資料 守望的澱粉質略解 粉紅兔大佬的澱粉質 演算法步驟 計算重心位置 計算答案 分治子問題繼續求解 1 3 include include include include include include include include include...

POJ 1741 點分治入門

tree 其實關於時間複雜度我也似懂非懂,大概就是把一顆樹分層,然後每一層的操作大概是 n log n 然後我們每次找樹的重心,就可以分logn層,所以總時間複雜度大概是就是n log n 關於路徑,我們先算出到根的距離,然後分成兩種,一種關於經過n節點,這是合法的,一種是不經過根節點,就是在一根子...

POJ1741 點分治模板

傳送門 求樹上兩點間路徑長度小於k的點對個數 參考資料 守望的澱粉質略解 粉紅兔大佬的澱粉質 演算法步驟 計算重心位置 計算答案 分治子問題繼續求解 1 3 include include include include include include include include include...