点分树

点分树是按照树重心构建的重构树,它可以解决静态点分治无法解决的带修问题,这里带修只能对节点性质修改,不能改变树的结构.

根据树重心的性质,点分树的深度是 \(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;
}
posted @ 2026-08-13 20:23  kzssCCC  阅读(5)  评论(0)    收藏  举报