點分治
學習鏈接
通過這個blog入門的:https://www.luogu.org/blog/user9012/dian-fen-zhi-lve-xie
簡介
在做一類樹上的題目時,往往需要對樹進行分治,能將子樹分解成大小盡量相等的情況是最吼的,因此我們每次選擇根節點的時候都應該是當前部分的重心.
因此點分治模板包含了兩個主要的過程:尋找當前部分的根和點分治主遞歸.
算法模板
尋找重心
1.,表示子樹大小.
2.的點不能訪問,這限制了重心只能在當前部分尋找.
3.,表示以爲根節點最大子樹大小.
4.,表示當前部分的重心.
5.,表示當前部分的大小.
int siz[N],use[N],big[N],rt,all;
void getroot(int u,int fa) {
siz[u] = 1;big[u] = 0;
for(auto p : G[u]) {
int v = p.first;
if(use[v] || v == fa) continue;
getroot(v,u);
siz[u] += siz[v];
big[u] = std::max(big[u],siz[v]);
}
big[u] = std::max(big[u],all-siz[u]);
if(big[u] < big[rt]) rt = u;
}
代碼實現比較簡單,很容易理解.
點分治主模板
void dfs(int u) {
//calc過程是求解與u有關的問題.
use[u] = 1;calc(u);
for(auto p : G[u]) {
int v = p.first;
if(use[v]) continue;
rt = 0;all = siz[v];
//尋找子問題的重心,並遞歸處理
getroot(v,u);dfs(rt);
}
}
例題
P3806 點分治模板題
對的每顆子樹記錄一個,裏面存這個子樹中的點到節點的距離可能值.
對節點存一個,表示遍歷過的子樹中的點到點的可行距離.
每回溯完一顆子樹後,先判斷該子樹能否與之前的子樹形成距離爲的點對,然後再把該子樹的合併到中去.
代碼只貼部分
void calc(int u) {
std::unordered_set<int> fin;
fin.insert(0);
for(auto p : edge[u]) {
int v = p.first;
int c = p.second;
if(use[v]) continue;
std::unordered_set<int> child;
getdis(child,v,u,c);
for(auto x : child) {
rep(i,1,m) {
if(fin.count(ask[i] - x)) {
ans[i] = 1;
}
}
}
for(auto x : child)
fin.insert(x);
}
}
CF161D 求距離爲k的點對數
同上一題,將改爲,其中表示距離爲的點有多少個即可.
void calc(int u) {
map fin;
fin[0] = 1;
for(auto v : G[u]) {
if(use[v]) continue;
map child;
getdis(child,v,u,1);
for(auto p : child) {
ans += fin[k-p.first] * p.second;
}
for(auto p : child) {
fin[p.first] += p.second;
}
}
}
P4178 求距離小於k的點的對數
還是使用來維護,只不過在計算答案的時候,需要用雙指針來掃描.
void calc(int u) {
map fin;
fin[0] = 1;
for(auto pp : G[u]) {
int v = pp.first,c = pp.second;
if(use[v]) continue;
map child;
getdis(child,v,u,c);
int sum = 0;
auto itf = fin.begin();
for(auto itc = child.rbegin();itc != child.rend();++itc) {
while(itf != fin.end() && itf->first + itc->first <= k) {
sum += itf->second;
++itf;
}
ans += sum * itc->second;
}
for(auto p : child) {
fin[p.first] += p.second;
}
}
}
P4149 求權值等於k的路徑最小邊數
這題也很簡單,僅僅需要修改一下表示的含義即可.
表示距離爲的路徑最少邊數.
void calc(int u) {
map fin;
for(auto pp : G[u]) {
int v = pp.first,c = pp.second;
if(use[v]) continue;
map child;
getdis(child,v,u,c,1);
for(auto p : child) {
if(p.first == k)
ans = std::min(ans,p.second);
else if(fin[k-p.first])
ans = std::min(ans,fin[k-p.first]+p.second);
}
for(auto p : child) {
if(fin[p.first] == 0)
fin[p.first] = p.second;
else
fin[p.first] = std::min(fin[p.first],p.second);
}
}
}