This documentation is automatically generated by online-judge-tools/verification-helper
View the Project on GitHub maspypy/library
#include "graph/tree_dp/rerooting_dp_2.hpp"
// 中間計算結果を全部メモリに持って, 可能な限り同じ計算を複数回しないようにする // メモリは増えるが一度の計算が重い場合には有効????? // 永続データ構造のマージのときに少しメモリが節約できたがわずか // https://qoj.ac/contest/1699/problem/8518 template <typename TREE, typename Data> struct Rerooting_dp_2 { static_assert(!TREE::Graph_type::is_directed); TREE &tree; vc<Data> dp; // full tree at v vc<Data> dp_1; // 辺 pv に対して、部分木 v vc<Data> dp_2; // 辺 pv に対して、部分木 p vc<Data> dp_3; // rootless pv vc<Data> dp_4; // dp_3 の左閉区間での累積 template <typename F1, typename F2, typename F3> Rerooting_dp_2(TREE &tree, F1 f_ee, F2 f_ev, F3 f_ve, const Data unit) : tree(tree) { build(f_ee, f_ev, f_ve, unit); } // v を根としたときの full tree Data operator[](int v) { return dp[v]; } // root を根としたときの部分木 v Data get(int v, int root) { if (root == v) return dp[v]; if (!tree.in_subtree(root, v)) { return dp_1[v]; } int w = tree.jump(v, root, 1); return dp_2[w]; } template <typename F1, typename F2, typename F3> void build(F1 f_ee, F2 f_ev, F3 f_ve, const Data unit) { using Edge = typename TREE::Graph_type::edge_type; int N = tree.N; // dp_1: subtree // dp_3: v までの左閉区間 dp_1.assign(N, unit); dp_3.assign(N, unit); dp_4.assign(N, unit); FOR_R(i, N) { int v = tree.V[i]; vc<Edge> ch; for (auto &e: tree.G[v]) if (e.to != tree.parent[v]) ch.eb(e); int n = len(ch); FOR(i, n) { auto &e = ch[i]; int c = e.to; dp_3[c] = f_ve(dp_1[c], e); dp_4[c] = (i == 0 ? dp_3[c] : f_ee(dp_4[ch[i - 1].to], dp_3[c])); } dp_1[v] = (n == 0 ? f_ev(unit, v) : f_ev(dp_4[ch[n - 1].to], v)); } // dp2[v]: subtree of p, rooted at v dp_2.assign(N, unit); // dp[v]: fulltree, rooted at v dp.assign(N, unit); FOR(i, N) { int v = tree.V[i]; vc<Edge> ch; Data x = unit; for (auto &e: tree.G[v]) { if (e.to != tree.parent[v]) ch.eb(e); if (e.to == tree.parent[v]) x = f_ve(dp_2[v], e); } int n = len(ch); FOR_R(i, n) { Data lprod = (i > 0 ? dp_4[ch[i - 1].to] : unit); dp_2[ch[i].to] = f_ev(f_ee(lprod, x), v); x = f_ee(x, dp_3[ch[i].to]); } dp[v] = f_ev(x, v); } } };
#line 1 "graph/tree_dp/rerooting_dp_2.hpp" // 中間計算結果を全部メモリに持って, 可能な限り同じ計算を複数回しないようにする // メモリは増えるが一度の計算が重い場合には有効????? // 永続データ構造のマージのときに少しメモリが節約できたがわずか // https://qoj.ac/contest/1699/problem/8518 template <typename TREE, typename Data> struct Rerooting_dp_2 { static_assert(!TREE::Graph_type::is_directed); TREE &tree; vc<Data> dp; // full tree at v vc<Data> dp_1; // 辺 pv に対して、部分木 v vc<Data> dp_2; // 辺 pv に対して、部分木 p vc<Data> dp_3; // rootless pv vc<Data> dp_4; // dp_3 の左閉区間での累積 template <typename F1, typename F2, typename F3> Rerooting_dp_2(TREE &tree, F1 f_ee, F2 f_ev, F3 f_ve, const Data unit) : tree(tree) { build(f_ee, f_ev, f_ve, unit); } // v を根としたときの full tree Data operator[](int v) { return dp[v]; } // root を根としたときの部分木 v Data get(int v, int root) { if (root == v) return dp[v]; if (!tree.in_subtree(root, v)) { return dp_1[v]; } int w = tree.jump(v, root, 1); return dp_2[w]; } template <typename F1, typename F2, typename F3> void build(F1 f_ee, F2 f_ev, F3 f_ve, const Data unit) { using Edge = typename TREE::Graph_type::edge_type; int N = tree.N; // dp_1: subtree // dp_3: v までの左閉区間 dp_1.assign(N, unit); dp_3.assign(N, unit); dp_4.assign(N, unit); FOR_R(i, N) { int v = tree.V[i]; vc<Edge> ch; for (auto &e: tree.G[v]) if (e.to != tree.parent[v]) ch.eb(e); int n = len(ch); FOR(i, n) { auto &e = ch[i]; int c = e.to; dp_3[c] = f_ve(dp_1[c], e); dp_4[c] = (i == 0 ? dp_3[c] : f_ee(dp_4[ch[i - 1].to], dp_3[c])); } dp_1[v] = (n == 0 ? f_ev(unit, v) : f_ev(dp_4[ch[n - 1].to], v)); } // dp2[v]: subtree of p, rooted at v dp_2.assign(N, unit); // dp[v]: fulltree, rooted at v dp.assign(N, unit); FOR(i, N) { int v = tree.V[i]; vc<Edge> ch; Data x = unit; for (auto &e: tree.G[v]) { if (e.to != tree.parent[v]) ch.eb(e); if (e.to == tree.parent[v]) x = f_ve(dp_2[v], e); } int n = len(ch); FOR_R(i, n) { Data lprod = (i > 0 ? dp_4[ch[i - 1].to] : unit); dp_2[ch[i].to] = f_ev(f_ee(lprod, x), v); x = f_ee(x, dp_3[ch[i].to]); } dp[v] = f_ev(x, v); } } };