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 的路徑 第二種情況可以通過遞迴來處理,我們直接來討論第一種情況 設當...