$\newcommand{\O}{\mathrm{O}}$

木上の頂点の値を (加算 / 更新), パスからの距離が $d$ 以内の頂点の値の (総和 / 最小値), 頂点 $u$ を根とする部分木内の頂点で $u$ からの距離が $d$ 以下であるものの値の (総和 / 最小値) などを処理するアルゴリズム.
yosupo さんのアイデア(参考 URL) を参考にさせていただき $1$ 層目に weighted balanced な木を用いた.
各クエリ $\O ( \log^2 n)$ 時間で処理するものの定数が重く, また空間計算量も $\O( n \log n)$ であるもののかなり大きくなる.
特に頂点更新 + 範囲最小値の方はオフラインの場合には事前に値を座圧し int 型の値として処理した方が空間計算量的に良い
(空間計算量削減のためセグ木の代わりに BIT $2$ つで RMQ を処理するテクを用いてみたが実測が遅くなってしまった).
(関数)
vertex_add($u$, $x$) / vertex_update($u$, $x$): 頂点 $u$ の値に $x$ を加算する / 頂点 $u$ の値を $x$ に更新する.
path_query($u$, $v$, $d$): 頂点 $u$, $v$ を結ぶパスからの距離が $d$ 以内の頂点についてクエリに答える.
subtree_query($u$, $d$): 頂点 $u$ を根とする部分木内の頂点で $u$ からの距離が $d$ 以下であるものについてクエリに答える.
例えば path_query($u$, $v$, $0$) は path query, path_query($u$, $u$, $d$) は ball query に対応する.
時間計算量: 構築 $\O( n \log n)$, 各クエリ $\O(\log^2 n)$, 空間計算量 $\O( n \log n)$
template<typename T> class BIT {
private:
int n;
vector<T> bit;
T sm;
public:
void init(const vector<T>& v){
n = (int)v.size() + 1, bit.resize(n, 0);
sm = accumulate(v.begin(), v.end(), (T)0);
int j;
for(int i = 1; i < n; ++i){
bit[i] += v[i - 1], j = i + (i & -i);
if(j < n) bit[j] += bit[i];
}
}
void add(int i, const T x){
++i, sm += x;
while(i < n) bit[i] += x, i += i & -i;
}
T sum(int i) const {
if(i == -1) return 0;
else if(i == n - 2) return sm;
else{
++i;
T s = 0;
while(i > 0) s += bit[i], i -= i & -i;
return s;
}
}
};
template<typename T> class HLdecomposition {
private:
struct node {
int left, right, lb, rb;
BIT<T> bt[3];
vector<pair<int, int> > pvec[3];
};
int V;
vector<vector<int> > G;
vector<node> nodes;
vector<T> value;
vector<int> sz, depth, pathup, path_id, node_id, wbt_root;
void predfs(const int u, const int p, const int d){
sz[u] = 1, depth[u] = d;
for(int& v : G[u]){
if(v == p){
if(v == G[u].back()) break;
else swap(v, G[u].back());
}
predfs(v, u, d + 1);
sz[u] += sz[v];
if(sz[v] > sz[G[u][0]]) swap(v, G[u][0]);
}
}
void dfs(const int u, const int p, const int par, int& tm, vector<vector<int> >& hpath){
for(const int v : G[u]){
if(v == p) continue;
if(v == G[u][0]){
pathup[v] = par, path_id[v] = tm, hpath[tm].push_back(v);
dfs(v, u, par, tm, hpath);
}else{
pathup[v] = u, path_id[v] = ++tm, hpath.push_back({v});
dfs(v, u, u, tm, hpath);
}
}
}
void node_merge(BIT<T>& bt, vector<pair<int, int> >& p, const vector<pair<int, int> >& q, const vector<pair<int, int> >& r){
const int n = (int)q.size() + (int)r.size();
p.resize(n);
merge(q.begin(), q.end(), r.begin(), r.end(), p.begin());
vector<T> val(n);
for(int i = 0; i < n; ++i) val[i] = value[p[i].second];
bt.init(val);
}
int build_rec(const int l, const int r, const vector<int>& vers, const vector<long long>& sm, const vector<vector<vector<int> > >& info, int& id){
const int k = id++;
nodes[k].lb = l, nodes[k].rb = r;
if(r - l == 1){
const int u = vers[l];
for(int i = 0; i < 3; ++i) nodes[k].pvec[i].resize(sz[u]);
vector<T> val(sz[u]);
int id = 0;
for(int i = 0; i < (int)info[u].size(); ++i){
for(const int v : info[u][i]){
nodes[k].pvec[0][id] = {i - l, v}, nodes[k].pvec[1][id] = {i, v}, nodes[k].pvec[2][id] = {i + l, v};
val[id++] = value[v];
}
}
for(int i = 0; i < 3; ++i) nodes[k].bt[i].init(val);
return node_id[u] = k;
}
int mid = l + 1;
long long cri = min(sm[mid] - sm[l], sm[r] - sm[mid]);
for(int i = l + 2; i < r; ++i){
if(cri < min(sm[i] - sm[l], sm[r] - sm[i])){
mid = i, cri = min(sm[mid] - sm[l], sm[r] - sm[mid]);
}
}
const int left = build_rec(l, mid, vers, sm, info, id), right = build_rec(mid, r, vers, sm, info, id);
nodes[k].left = left, nodes[k].right = right;
for(int i = 0; i < 3; ++i) node_merge(nodes[k].bt[i], nodes[k].pvec[i], nodes[left].pvec[i], nodes[right].pvec[i]);
return k;
}
int build_wbt(const vector<int>& vers, int& id, const vector<vector<vector<int> > >& info){
int n = (int)vers.size();
vector<long long> sm(n + 1, 0);
for(int i = 0; i < n; ++i) sm[i + 1] = sm[i] + sz[vers[i]];
return build_rec(0, (int)vers.size(), vers, sm, info, id);
}
void build_impl(const int root){
predfs(root, -1, 0);
int tm = 0;
vector<vector<int> > hpath;
pathup[root] = -1, path_id[root] = 0, hpath.push_back({root});
dfs(root, -1, -1, tm, hpath);
vector<vector<vector<int> > > info(V);
for(int i = 0; i < V; ++i){
const int d = depth[i];
for(int u = i; u >= 0; u = pathup[u]){
if((int)info[u].size() <= d - depth[u]) info[u].resize(d - depth[u] + 1);
info[u][d - depth[u]].push_back(i);
}
}
int id = 0;
for(const vector<int>& path : hpath){
for(int i = 0; i < (int)path.size() - 1; ++i) sz[path[i]] -= sz[path[i + 1]];
wbt_root.push_back(build_wbt(path, id, info));
}
}
void vertex_add_impl(const int u, const T x){
for(int v = u; v >= 0; v = pathup[v]){
const int d = depth[u] - depth[v];
const int id = nodes[node_id[v]].lb;
int w = wbt_root[path_id[v]];
while(true){
node& cur = nodes[w];
cur.bt[0].add((int)(lower_bound(cur.pvec[0].begin(), cur.pvec[0].end(), make_pair(d - id, u)) - cur.pvec[0].begin()), x);
cur.bt[1].add((int)(lower_bound(cur.pvec[1].begin(), cur.pvec[1].end(), make_pair(d, u)) - cur.pvec[1].begin()), x);
cur.bt[2].add((int)(lower_bound(cur.pvec[2].begin(), cur.pvec[2].end(), make_pair(d + id, u)) - cur.pvec[2].begin()), x);
if(nodes[w].rb - nodes[w].lb == 1) break;
else if(id < nodes[nodes[w].left].rb) w = nodes[w].left;
else w = nodes[w].right;
}
}
}
T query(int a, int b, const int x, const node& cur, int t) const {
const int l = cur.lb, r = cur.rb;
if(r <= a || b <= l) return 0;
if(a <= l && r <= b){
return cur.bt[t].sum((int)(upper_bound(cur.pvec[t].begin(), cur.pvec[t].end(), make_pair(x, V)) - cur.pvec[t].begin()) - 1);
}else{
return query(a, b, x, nodes[cur.left], t) + query(a, b, x, nodes[cur.right], t);
}
}
T path_query_impl(int u, int v, const int d) const {
T res = 0;
while(path_id[u] != path_id[v]){
if(path_id[u] < path_id[v]){
const int id = nodes[node_id[v]].lb;
const node& wroot = nodes[wbt_root[path_id[v]]];
res += query(0, id + 1, d, wroot, 1) + query(id + 1, wroot.rb, d + id, wroot, 2) - query(0, wroot.rb, d - 1, wroot, 2);
v = pathup[v];
}else{
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res += query(0, id + 1, d, wroot, 1) + query(id + 1, wroot.rb, d + id, wroot, 2) - query(0, wroot.rb, d - 1, wroot, 2);
u = pathup[u];
}
}
if(depth[u] > depth[v]) swap(u, v);
const int L = nodes[node_id[u]].lb, R = nodes[node_id[v]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res += query(0, L, d - L, wroot, 0) + query(L, R + 1, d, wroot, 1) + query(R + 1, wroot.rb, d + R, wroot, 2);
const int lca = depth[u];
int prev_wbt_root = wbt_root[path_id[u]];
for(u = pathup[u]; u >= 0 && depth[u] >= lca - d; prev_wbt_root = wbt_root[path_id[u]], u = pathup[u]){
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res += query(0, id + 1, d - lca + depth[u] - id, wroot, 0) + query(id + 1, wroot.rb, d - lca + depth[u] + id, wroot, 2);
const node& prev_wroot = nodes[prev_wbt_root];
res -= query(0, prev_wroot.rb, d - lca + depth[u] - 1, prev_wroot, 2);
}
return res;
}
T subtree_query_impl(const int u, const int d) const {
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
return query(id, wroot.rb, d + id, wroot, 2);
}
public:
HLdecomposition(const vector<T>& _value)
: V((int)_value.size()), G(V), nodes(2 * V - 1), value(_value),
sz(V), depth(V), pathup(V), path_id(V), node_id(V){}
void add_edge(const int u, const int v){
G[u].push_back(v), G[v].push_back(u);
}
void build(const int root=0){
build_impl(root);
}
void vertex_add(const int u, const T x){
vertex_add_impl(u, x);
}
T path_query(const int u, const int v, const int d) const {
assert(d >= 0);
return path_query_impl(u, v, d);
}
T subtree_query(const int u, const int d) const {
assert(d >= 0);
return subtree_query_impl(u, d);
}
};
template<typename T> class segtree {
private:
int n, sz;
vector<T> node;
public:
void init(const vector<T>& v){
n = 1, sz = (int)v.size();
while(n < sz) n *= 2;
node.assign(2 * n, numeric_limits<T>::max());
for(int i = 0; i < sz; ++i) node[i + n] = v[i];
for(int i = n - 1; i >= 1; --i) node[i] = min(node[i << 1], node[(i << 1) | 1]);
}
void update(int k, const T a){
const T old = node[k += n];
if(old == a) return;
node[k] = a;
while(k >>= 1){
if(node[k] > a) node[k] = a;
else if(node[k] == old) node[k] = min(node[k << 1], node[(k << 1) | 1]);
else break;
}
}
T query(int a, int b) const {
if(a == 0 && b == sz) return node[1];
T res1 = numeric_limits<T>::max(), res2 = numeric_limits<T>::max();
a += n, b += n;
while(a != b){
if(a & 1) res1 = min(res1, node[a++]);
if(b & 1) res2 = min(node[--b], res2);
a >>= 1, b >>= 1;
}
return min(res1, res2);
}
};
template<typename T> class HLdecomposition {
private:
struct node {
int left, right, lb, rb;
segtree<T> seg[3];
vector<pair<int, int> > pvec[3];
};
int V;
vector<vector<int> > G;
vector<node> nodes;
vector<T> value;
vector<int> sz, depth, pathup, path_id, node_id, wbt_root;
void predfs(const int u, const int p, const int d){
sz[u] = 1, depth[u] = d;
for(int& v : G[u]){
if(v == p){
if(v == G[u].back()) break;
else swap(v, G[u].back());
}
predfs(v, u, d + 1);
sz[u] += sz[v];
if(sz[v] > sz[G[u][0]]) swap(v, G[u][0]);
}
}
void dfs(const int u, const int p, const int par, int& tm, vector<vector<int> >& hpath){
for(const int v : G[u]){
if(v == p) continue;
if(v == G[u][0]){
pathup[v] = par, path_id[v] = tm, hpath[tm].push_back(v);
dfs(v, u, par, tm, hpath);
}else{
pathup[v] = u, path_id[v] = ++tm, hpath.push_back({v});
dfs(v, u, u, tm, hpath);
}
}
}
void node_merge(segtree<T>& sg, vector<pair<int, int> >& p, const vector<pair<int, int> >& q, const vector<pair<int, int> >& r){
const int n = (int)q.size() + (int)r.size();
p.resize(n);
merge(q.begin(), q.end(), r.begin(), r.end(), p.begin());
vector<T> val(n);
for(int i = 0; i < n; ++i) val[i] = value[p[i].second];
sg.init(val);
}
int build_rec(const int l, const int r, const vector<int>& vers, const vector<long long>& sm, const vector<vector<vector<int> > >& info, int& id){
const int k = id++;
nodes[k].lb = l, nodes[k].rb = r;
if(r - l == 1){
const int u = vers[l];
for(int i = 0; i < 3; ++i) nodes[k].pvec[i].resize(sz[u]);
vector<T> val(sz[u]);
int id = 0;
for(int i = 0; i < (int)info[u].size(); ++i){
for(const int v : info[u][i]){
nodes[k].pvec[0][id] = {i - l, v}, nodes[k].pvec[1][id] = {i, v}, nodes[k].pvec[2][id] = {i + l, v};
val[id++] = value[v];
}
}
for(int i = 0; i < 3; ++i) nodes[k].seg[i].init(val);
return node_id[u] = k;
}
int mid = l + 1;
long long cri = min(sm[mid] - sm[l], sm[r] - sm[mid]);
for(int i = l + 2; i < r; ++i){
if(cri < min(sm[i] - sm[l], sm[r] - sm[i])){
mid = i, cri = min(sm[mid] - sm[l], sm[r] - sm[mid]);
}
}
const int left = build_rec(l, mid, vers, sm, info, id), right = build_rec(mid, r, vers, sm, info, id);
nodes[k].left = left, nodes[k].right = right;
for(int i = 0; i < 3; ++i) node_merge(nodes[k].seg[i], nodes[k].pvec[i], nodes[left].pvec[i], nodes[right].pvec[i]);
return k;
}
int build_wbt(const vector<int>& vers, int& id, const vector<vector<vector<int> > >& info){
int n = (int)vers.size();
vector<long long> sm(n + 1, 0);
for(int i = 0; i < n; ++i) sm[i + 1] = sm[i] + sz[vers[i]];
return build_rec(0, (int)vers.size(), vers, sm, info, id);
}
void build_impl(const int root){
predfs(root, -1, 0);
int tm = 0;
vector<vector<int> > hpath;
pathup[root] = -1, path_id[root] = 0, hpath.push_back({root});
dfs(root, -1, -1, tm, hpath);
vector<vector<vector<int> > > info(V);
for(int i = 0; i < V; ++i){
const int d = depth[i];
for(int u = i; u >= 0; u = pathup[u]){
if((int)info[u].size() <= d - depth[u]) info[u].resize(d - depth[u] + 1);
info[u][d - depth[u]].push_back(i);
}
}
int id = 0;
for(const vector<int>& path : hpath){
for(int i = 0; i < (int)path.size() - 1; ++i) sz[path[i]] -= sz[path[i + 1]];
wbt_root.push_back(build_wbt(path, id, info));
}
}
void vertex_update_impl(const int u, const T x){
for(int v = u; v >= 0; v = pathup[v]){
const int d = depth[u] - depth[v];
const int id = nodes[node_id[v]].lb;
int w = wbt_root[path_id[v]];
while(true){
node& cur = nodes[w];
cur.seg[0].update((int)(lower_bound(cur.pvec[0].begin(), cur.pvec[0].end(), make_pair(d - id, u)) - cur.pvec[0].begin()), x);
cur.seg[1].update((int)(lower_bound(cur.pvec[1].begin(), cur.pvec[1].end(), make_pair(d, u)) - cur.pvec[1].begin()), x);
cur.seg[2].update((int)(lower_bound(cur.pvec[2].begin(), cur.pvec[2].end(), make_pair(d + id, u)) - cur.pvec[2].begin()), x);
if(nodes[w].rb - nodes[w].lb == 1) break;
else if(id < nodes[nodes[w].left].rb) w = nodes[w].left;
else w = nodes[w].right;
}
}
}
T query(int a, int b, const int x, const node& cur, int t) const {
const int l = cur.lb, r = cur.rb;
if(r <= a || b <= l) return numeric_limits<T>::max();
if(a <= l && r <= b){
return cur.seg[t].query(0, (int)(upper_bound(cur.pvec[t].begin(), cur.pvec[t].end(), make_pair(x, V)) - cur.pvec[t].begin()));
}else{
return min(query(a, b, x, nodes[cur.left], t), query(a, b, x, nodes[cur.right], t));
}
}
T path_query_impl(int u, int v, const int d) const {
T res = numeric_limits<T>::max();
while(path_id[u] != path_id[v]){
if(path_id[u] < path_id[v]){
const int id = nodes[node_id[v]].lb;
const node& wroot = nodes[wbt_root[path_id[v]]];
res = min({res, query(0, id + 1, d, wroot, 1), query(id + 1, wroot.rb, d + id, wroot, 2)});
v = pathup[v];
}else{
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res = min({res, query(0, id + 1, d, wroot, 1), query(id + 1, wroot.rb, d + id, wroot, 2)});
u = pathup[u];
}
}
if(depth[u] > depth[v]) swap(u, v);
const int L = nodes[node_id[u]].lb, R = nodes[node_id[v]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res = min({res, query(0, L, d - L, wroot, 0), query(L, R + 1, d, wroot, 1), query(R + 1, wroot.rb, d + R, wroot, 2)});
const int lca = depth[u];
for(u = pathup[u]; u >= 0 && depth[u] >= lca - d; u = pathup[u]){
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
res = min({res, query(0, id + 1, d - lca + depth[u] - id, wroot, 0), query(id + 1, wroot.rb, d - lca + depth[u] + id, wroot, 2)});
}
return res;
}
T subtree_query_impl(const int u, const int d) const {
const int id = nodes[node_id[u]].lb;
const node& wroot = nodes[wbt_root[path_id[u]]];
return query(id, wroot.rb, d + id, wroot, 2);
}
public:
HLdecomposition(const vector<T>& _value)
: V((int)_value.size()), G(V), nodes(2 * V - 1), value(_value),
sz(V), depth(V), pathup(V), path_id(V), node_id(V){}
void add_edge(const int u, const int v){
G[u].push_back(v), G[v].push_back(u);
}
void build(const int root=0){
build_impl(root);
}
void vertex_update(const int u, const T x){
vertex_update_impl(u, x);
}
T path_query(const int u, const int v, const int d) const {
assert(d >= 0);
return path_query_impl(u, v, d);
}
T subtree_query(const int u, const int d) const {
assert(d >= 0);
return subtree_query_impl(u, d);
}
};
手元でかなりテストはした.