點分樹和點分治好像一直是 oi 中的熱門考點,出現頻率並不算太低,而且為了迎合自己的學習清單計畫,這裡大概就是說每學乙個就會寫一篇部落格來記錄一下自己真的學過這玩意。
點分樹是在點分治的基礎之上,通過資料結構去維護樹內聯通塊的資訊,維護深度相關的資訊。
分治重心有良好的性質,那就是分治之後乙個子樹仍然是乙個連通塊,那麼我們就可以繼續分治下去。
根據重心的性質,我們最終的點分樹最多有 \(\lg n\) 層,每一層都記錄相關子樹的深度資訊,從而達到維護樹上點與點之間距離的效果。
這是乙個實現的較為優秀的點分樹建樹過程,建樹的時間複雜度和空間複雜度都是 \(o(n\lg n)\) 的。namespace pdt
// inline int lbt(int x)
// inline void add(int x, int k)
// inline int chk(int x, int k, int ret = 0)
// } f[n], g[n];
int mx[n], sz[n], dep[n], d[n][20], fa[n];
inline void chk(int &x, int y)
int frt(int x, int al, int pre = 0, int t = 0)
void sol(int x, int d, int &dx, int &df, int pre = 0)
void dfs(int x, int d, int w, int pre = 0, int dx = 0, int df = 0)
void init()
}
建出點分樹之後的內容就是八仙過海各顯神通了。
從簡單的模板題入手。
這道題要求我們修改某乙個點的權值,然後求乙個點 \(k\) 距離以內的所有點的權值和。
維護每乙個分治重心的子樹下所有深度的點的資訊,然後再維護其分治重心父親在當前子樹下所有深度的點的資訊,每一次查詢就可以通過查詢其所有的祖先節點來進行操作了,時間複雜度是 \(o(\lg^2 n)\) 的。
**:
練習題:爍爍的遊戲namespace pdt
inline void bld(int s)
inline void add(int x, int k)
inline int ask(int x, int ret = 0)
} f[n], g[n];
int mx[n], sz[n], fa[n], d[n][20], dep[n];
inline void check(int &x, int y)
int frt(int x, int al, int pre = 0, int t = 0)
int dx = 0, dw = 0;
void sol(int x, int d, int pre = 0)
void dfs(int x, int d, int w, int pre = 0)
void upd(int x, int y)
int qry(int x, int k)
void init()
}
不過是將查詢塊單點修變成修改塊單點查,同樣的套路,在查詢處稍作改變即可。
練習題:atm 的樹void mdf(int x, int k, int w)
} int qry(int x)
return ret;
}
我們可以二分乙個答案,然後判斷這個答案內的距離有多少個,維護距離可以用平衡樹維護。
有時候碼風有一定差異請見諒。namespace pdt
int frt(int x, int al, int pre = 0, int t = 0, int v = 0)
void sol(int x, int d, int tp, int dis = 0, int pre = 0, int v = 0)
void dfs(int x, int d, int w, int pre = 0, int v = 0)
inline void init()
inline int ask(int x, int y)
}inline void solve()
pdt::init();
for(int i = 1; i <= n; i++)
cout << ans + 1 << '\n';
} return ;
}
不過我的寫法好像有一點小問題,是會被叉掉的,眼尖的同學可以隨便叉一下,我過了就懶得改了。
例題:[hnoi2015]開店
實際上這一題不用維護深度資訊,我們需要維護的是點權大小,所以我們以點權為關鍵字排序,得到乙個遞增的點權大小資訊,然後做乙個字尾和即可。
為什麼是字尾和?因為字首和在查詢的時候會有一點麻煩,字尾和可以減去一下邊界的特判。
然後按照題目要求隨便做做就可以了。
練習題:book of evilnamespace pdt
int frt(int x, int al, int pre = 0, int t = 0, int v = 0)
void sol(int x, int d, int tp, ll dis = 0, int pre = 0, int v = 0)
void dfs(int x, int d, int w, int pre = 0, int v = 0)
void init()
inline pairqryf(int x, int l, int r)
inline pairqryg(int x, int l, int r)
ll ask(int x, int l, int r)
return ret;
}}
看完上面的題面做這題反而有點小兒科了,有兩種做法,比較無腦的點分樹做法就是直接建點分樹然後判斷距離 \(d\) 以內是否有 \(m\) 個點既可。
**不貼了……
例題:[zjoi2015]幻想鄉戰略遊戲
本題要求帶權重心,不難發現資料範圍中的重要性質,就是乙個點的度數最多為 \(20\)。
所以我們考慮暴力轉移,每乙個點只會列舉 \(20\) 次轉移,整體的查詢時 \(o(dep)\) 的,所以我們用從重心列舉的思想,於是就有 \(o(dep) = o(\lg n)\) 的複雜度,這是我們可以接受的。
接下來就是要快速求出乙個點的權值,我們可以用點分樹輕鬆維護這個問題。
時間複雜度 \(o(n\lg^2 n)\)。
記得開namespace pdt
int frt(int x, int al, int pre = 0, int t = 0, int v = 0)
void sol(int x, int d, int tp, int dis = 0, int pre = 0, int v = 0)
void mkt(int x, int d, int w, int pre = 0, int v = 0)
void init()
void upd(int x, ll k)
return ;
} ll ask(int x)
return ret;
} inline ll fnd(int x, int d, int v = 0)
}
long long
。
練習題:小清新資料結構題
假設此時 \(s_i\) 為以 \(x\) 為根的時候的子樹大小,我們需要求的就是 \(\sum\limits_^n s_i^2\)。
本題之後,這就是乙個套路式子了,我們可以很容易求出 \(\sum\limits_^ns_i\) 的值,不難發現就是 \(\sum\limits_^n a_i(dis(i,x)+1)\)。
拆開之後可以得到:
\[sum+\sum\limits_^na_i dis(i,x)
\]\(sum\) 指的是 \(\sum\limits_^n a_i\)。後面那個式子可以像幻想鄉戰略遊戲一樣用點分樹維護。
接著我們需要證明,無論根是哪個點, \(\sum\limits_^ns_i(sum-s_i)\) 都是一定的。
實際上這就是列舉每一條邊,然後將左右邊的子樹權值和乘起來,我們開啟乘法之後可以得到:\(\sum\limits_dis(i,j) a_ia_j\)。
然後我們就可以發現這玩意確實和根是啥無關,所以我們得到答案就是:
\[sum\sum_^ns_i- \sum_^ns_i(sum - s_i)
\]前者中 \(\sum\limits_^ns_i\) 已經在上面有證明了,可以 \(o(\lg n)\) 完成修改和查詢,後者可以考慮處理 \(\sum\limits_dis(i,j)a_ia_j\)。
時間複雜度 \(o(n\lg n)\),然後就被樹剖 \(o(n\lg^2n)\) 給爆錘了。
namespace pdt
int frt(int x, int al, int pre = 0, int t = 0)
void sol(int x, int d, int pre = 0)
void dfs(int x, int d, int w, int pre = 0)
void init()
ll calc(int x)
return ret;
} void upd(int x, ll k)
return ;
} ll ask(int x)
}
樹鏈剖分學習小記
入門題 zjoi2008 樹的統計 description 一棵樹上有n個節點,編號分別為1到n,每個節點都有乙個權值w。我們將以下面的形式來要求你對這棵樹完成一些操作 i.change u t 把結點u的權值改為t ii.qmax u v 詢問從點u到點v的路徑上的節點的最大權值 iii.qsum...
樹鏈剖分 模板小記
樹鏈剖分 模板小記 q.樹鏈剖分的背景?a.連通且無環的樹 q.樹鏈剖分的作用?a.大幅增加碼量 a.將一棵樹變成幾條鏈 樹形變成線形 減少處理難度 q.樹鏈剖分能處理哪些問題?a 1.修改的有 將 x 節點 or times val 將 x 到 y 這條路徑上所有節點 or times val 將...
維護點分樹
總的來說,維護點分樹需要記錄以下這些東西 該點在點分樹上的每一級父親。它在每一級父親那裡的深度。它和它父親之間的cut點。如果要維護資料結構,需要該求解的答案可減,並且需要對於每乙個點分中心記錄乙個該點分中心為中心的資料結構,以及以該割點為點分中心的資料結構,查詢時相減即可。可以用來維護一系列求解路...