title | documentation_of |
---|---|
Guni (Sack) / DSU on tree (根付き木の全ての部分木を経由するような頂点追加・削除操作列の生成) |
./guni.hpp |
頂点数
-
( は から一個の頂点を追加または削除したもの) - 各部分木について,それに含まれる頂点集合と
が一致するような が存在する
本コードはこのような
木の上で DFS し,行きがけに頂点追加・帰りがけに頂点削除を行うのが基本方針だが,一部の頂点には複数回訪問する点で通常の DFS とは異なる.
Heavy-light decomposition を行う.ある頂点
-
と light edge で繋がる子の部分木全てについて再帰的に DFS する -
と heavy edge で繋がる子の部分木について再帰的に DFS する(ただし, 手順 5. の削除操作を省く) -
light edge で繋がる子の部分木に含まれる全頂点を追加する - 頂点
を追加する - (
ができる) -
の部分木に含まれる全頂点を削除する
このアルゴリズムは以下の背景により
- 手順 3. の計算量は weighted-union heuristic (いわゆるマージテク)により
である. - 各頂点が手順 6. で削除対象になる回数は,その頂点から根への単純パスに存在する(distinct な) heavy path の本数と一致するので, 6. の計算量は
である.
本テクニックを使用するまでもない例だが,根付き木の全ての部分木について「部分木を構成する頂点の id
の 2 乗和」は以下のように実装できる.
int N, root;
vector<pair<int, int>> edges(N - 1);
guni g(N);
for (auto [u, v] : edges) g.add_bi_edge(u, v);
std::vector<long long> ret(N);
long long sum_of_i_quads = 0;
auto Add = [&](int i) { sum_of_i_quads += (long long)i * i; };
auto Remove = [&](int i) { sum_of_i_quads -= (long long)i * i; };
auto Solve = [&](int i) { ret.at(i) = sum_of_i_quads; };
g.run(0, Add, Remove, Solve);