點分治學習筆記

2022-08-18 23:09:17 字數 1741 閱讀 3089

tree

給定一顆\(n\)點邊帶權無根樹,試問樹上距離小於\(k\)點對有幾何?

本題為點分治板子題.點分治是一種十分重要的演算法,運用了容斥原理和分治思想,其基本思路如下:

題目求的是滿足條件的樹上路徑的條數,我們先對樹上路徑進行分類:

一:經過根節點的路徑

二:不經過根節點的路徑

對於經過根節點的路徑,我們可以將其拆分為經過其不同子節點的路徑進行計算.至於不經過根節點的路徑,我們將其視為經過以某一節點為根節點的子樹的根節點的路徑,於是乎,我們成功將問題拆分為兩個不相交的子問題,先處理出問題一,然後遞迴進子樹,將問題二轉化為子樹的問題一即可.

為了保證演算法複雜度,我們需要尋找乙個合適的點作為根.這裡,我們選用重心作為根,複雜度計算如下:

先介紹乙個定理:對於以樹的重心為根的有根樹,其最大子樹大小不超過\(\frac\).

反證法證明如下:假設超過了,其最大子樹大小\(k>\frac\),那麼將重心往這個子樹方向移動,其最大子樹一定變小,證畢 

於是我們遞迴的次數是\(\log n\)級別的,複雜度不超過\(\omicron(n \log^ n)\)

於是乎,我們有如下流程:

首先預處理出當前計算的樹的重心,然後根據重心進行計算,算出結果後包含不符合的情況(計算兩點同子樹),在遍歷子樹時順便減掉,然後再一一遍歷子樹即可.

#include#include#include#include#include#include#include#include#include#define r register

#define next kdjadskfj

#define debug puts("mlg")

#define mod 1000000009

#define mod(x) ((x%mod+mod)%mod)

using namespace std;

typedef long long ll;

typedef long double ld;

typedef unsigned long long ull;

inline ll read();

inline void write(ll x);

inline void writeln(ll x);

inline void writesp(ll x);

ll n,k;

ll head[200000],next[200000],tot,to[200000],c[200000];

ll root,vis[200000],tsiz,size[200000],wt[200000];

ll rem[200000],cnt;

ll ans;

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

inline void getroot(ll x,ll fa)

} wt[x]=max(wt[x],tsiz-size[x]);

if(wt[root]>wt[x]) root=x;

}inline void dfs(ll x,ll d,ll fa)

}inline ll calc(ll x,ll d)while(ch>='0'&&ch<='9')return x*t;}

inline void write(ll x)if(x<=9)write(x/10);putchar(x%10+'0');}

inline void writesp(ll x)

inline void writeln(ll x)

點分治學習筆記

點分治主要用來處理樹上路徑問題,可以統計樹上點到點的所有路徑,複雜度o nlogn 基於樹上的結點進行分治,不斷將一棵樹拆成多顆子樹處理 選擇點時為了防止退化成鏈的情況,如果選點後左右子樹越大,遞迴層數越多,時間越慢,反之則越快,我們每次選擇子樹內的重心 void getroot int u,int...

點分治學習筆記

關於點分治,其實思想是非常好理解的,模擬在數列上或是在平面上的分治演算法 如歸併排序,平面最近點對等 我們可以從字面上理解該演算法 以乙個點為界限,將一棵樹分成若干個子樹,當劃分到一定規模,就對每個子樹分別進行求解 感性理解就好了 感受乙個演算法最直觀的辦法,就是來看一道模板題。給定一棵有 n 個點...

點分治學習筆記

point divide and rule 澱粉質就是在樹上,依靠不停的遞迴和分治,解決相同的子問題 先來看看模板題 tree 就是找樹上 k 的路徑有多少 我們可以分兩種情況討論 1.經過根節點 p 的路徑 2.不經過根節點 p 的路徑 第二種情況可以通過遞迴來處理,我們直接來討論第一種情況 設當...