實在拖得太久了。
先扔掉資料
分治的核心是盡量把乙個整體分成接近的兩個部分,這樣遞迴處理可以讓複雜度從n²變成nlogn。
兩個問題,如何區分和如何算答案。
對於第乙個問題,重心,然後就是找重心的方法,兩個dfs,
對於第二個問題,對於每個重心算當前塊中每個點到重心的答案,然後由重心分開的塊要把多餘的資訊去掉。
求出重心的兩個dfs
第一,先算出每個點的子樹大小
void dfs_size(int x,intfa) }
}
第二,如果把乙個點作為重心,它會把當前的聯通塊分成兩個部分,子樹和整個聯通段的大小-子樹,通過第二個dfs找到乙個可以把圖分成兩快大小平衡的連通圖
void dfs_find(int x,int fa,inty) }
if (msz[root]>msz[x]) root=x;
}
//這裡把root開成全域性變數
統計答案的more函式和dfs下去的calc函式
more函式用於計算答案,當需要維護動態資訊是,more函式用於一開始構建節點儲存資料結構的初始化。
ll more(int x,intlen)
//update
calc函式用於不斷遞迴下去就子樹
void calc(intx) }
}
如果子樹的資訊無法通過more直接減掉,可以分開寫calc和more
,即
void calc(intx) }
repedge(i,root)
}
附上點分治會用上的dfs_len函式,用於記錄長度資訊
void dfs_len(int x,int fa,intlen)
}
對於動態點分治,也是由點分治引出來的,顯然對於乙個點,他最多屬於logn個重心所在的塊,重心之間又是有層次關係,可以抽象構造乙個重心樹,用資料結構就可以啦。
比較麻煩的在於對於重心帶的資料結構,他的域要控制得和這個塊大小差不多,很多人用vec動態申請空間當bit用,我習慣用線段樹。
難點跟點分治一樣,在於剔除在同一棵子樹上的重複資訊,所以要構建兩個資料結構,乙個做加的,乙個做減的
構建完後,對於詢問或修改的某個點,依次訪問它從裡到外每個重心的統計資訊。
點分治與動態點分治
點分治一般是用於解決樹上路徑問題。樹的重心 把重心這個點割掉後,使所形成的最大的聯通塊大小最小的點。可以證明重心子樹的大小最大不會超過 n over 2 重心可以通過 dfs 一遍求出。maxsiz x 表示割掉點x後所形成的的最大的聯通塊的大小 void dfs int x,int fa max ...
動態點分治
由於蒟蒻太遜,現在才開始學動態點分治,寫乙個 blog 吧。動態點分治是利用點分治的過程,建成一顆由子樹重心連線而成的點分樹,這棵樹的高度為 log n 級別的,因此可以通過暴力跳父親完成修改操作。建立點分樹的過程,就是按照點分治的流程,記錄上級重心並連線,即可獲得一棵點分樹。點分樹的結構與原樹不相...
動態點分治
首先你要先會點分治,然後動態點分治就是把點分治可持久化一下,讓其不用再每次詢問時都重新做一遍 具體就是,你考慮做點分的時候,你在每個分治重心上統計它所管轄的所有點的資訊,並計算答案。那麼每個點的資訊只會出現在它上面的分治重心中,所以我們把每層分治重心向上一層的連邊,這樣每個點的資訊只會出現都在它所有...