点分树
点分树是按照树重心构建的重构树,它可以解决静态点分治无法解决的带修问题,这里带修只能对节点性质修改,不能改变树的结构.
根据树重心的性质,点分树的深度是 \(log\) 级别的,这个性质允许我们完成一些非常暴力的操作.
构建方法很简单,按照点分治的处理顺序连边即可,通常会额外维护每个节点到它所有祖先的距离.
更新时,沿着 \(fa\) 暴力往上更新即可.
在点分治中,我们是按照重心分类的,因此在点分树上的查询仍然是暴力遍历所有祖先.
例题:CF342E
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int INF = 1e9;
void solve(){
int n,m;
cin >> n >> m;
vector<vector<int>> adj(n+1);
for (int i=0;i<n-1;i++){
int u,v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
vector<int> sz(n+1),fa(n+1,-1);
vector<vector<pair<int,int>>> path(n+1);
vector<bool> vis(n+1,false);
function<void(int,int)> getsz = [&](int u,int par){
sz[u] = 1;
for (auto& v:adj[u]){
if (v==par || vis[v]) continue;
getsz(v,u);
sz[u] += sz[v];
}
};
auto getsent = [&](int u){
getsz(u,-1);
int half = sz[u]>>1;
int par = u;
while (1){
bool ok = false;
for (auto& v:adj[u]){
if (v==par || vis[v]) continue;
if (sz[v]>half){
ok = true;
par = u;
u = v;
break;
}
}
if (!ok) break;
}
return u;
};
function<void(int,int,int,int)> dfs = [&](int u,int par,int sent,int dis){
path[u].emplace_back(dis,sent);
for (auto& v:adj[u]){
if (v==par || vis[v]) continue;
dfs(v,u,sent,dis+1);
}
};
function<void(int)> work = [&](int u){
vis[u] = true;
path[u].emplace_back(0,u);
for (auto& v:adj[u]){
if (vis[v]) continue;
dfs(v,u,u,1);
}
for (auto& v:adj[u]){
if (vis[v]) continue;
int next = getsent(v);
fa[next] = u;
work(next);
}
};
work(getsent(1));
vector<int> best(n+1,INF);
auto update = [&](int x){
for (auto& [dis,y]:path[x]){
best[y] = min(best[y],dis);
}
};
auto query = [&](int x){
int res = INF;
for (auto& [dis,y]:path[x]){
res = min(res,dis+best[y]);
}
return res;
};
update(1);
while (m--){
int op,x;
cin >> op >> x;
if (op==1){
update(x);
}
else{
cout << query(x) << '\n';
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号