Skip to content

tree_path_affine_sum.hpp

SECTIONGraph INCLUDEnoya/tree_path_affine_sum.hpp

维护树上点的仿射函数,并查询一条有向路径上的函数复合结果。

Complexity: Time: O(n). Space: O(n), including O(n) recursion stack on a path.

AC 记录:tree_path_composite_sum

跳到代码 · GitHub ↗

Implementation

当前头文件,省略 include guard;依赖见 #include

/// @complexity Time: O(n).
/// Space: O(n), including O(n) recursion stack on a path.

#include <cassert>
#include <utility>
#include <vector>

namespace noya {

/// @brief Sum edge-affine path transforms from every possible root. For an
/// oriented edge x-y, the message sent by y is the sum of path values whose
/// destinations lie on y's side after deleting the edge. Applying bx+c to all
/// of them gives b times that message plus c times the side size. One postorder
/// pass builds child-side messages; one preorder reroot pass supplies the
/// complementary parent-side message.
template <class T>
std::vector<T> tree_path_affine_sums(const std::vector<T> &val,
                                     const std::vector<std::pair<int, int>> &ee,
                                     const std::vector<std::pair<T, T>> &aff) {
  int n = int(val.size());
  assert(ee.size() == aff.size());
  assert(int(ee.size()) == std::max(0, n - 1));
  if (n == 0) {
    return {};
  }
  struct edge {
    int to;
    T mul;
    T add;
  };
  std::vector<std::vector<edge>> g(n);
  for (int i = 0; i < n - 1; i++) {
    auto [a, b] = ee[i];
    auto [mul, add] = aff[i];
    g[a].push_back({b, mul, add});
    g[b].push_back({a, mul, add});
  }

  std::vector<int> siz(n);
  std::vector<T> dn(n);
  auto dfs = [&](auto &&self, int u, int fa) -> void {
    siz[u] = 1;
    dn[u] = val[u];
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      self(self, x.to, u);
      siz[u] += siz[x.to];
      dn[u] += x.mul * dn[x.to] + x.add * T(siz[x.to]);
    }
  };
  dfs(dfs, 0, -1);

  std::vector<T> ans(n);
  ans[0] = dn[0];
  auto df1 = [&](auto &&self, int u, int fa) -> void {
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      T cc = x.mul * dn[x.to] + x.add * T(siz[x.to]);
      T up = ans[u] - cc;
      T pc = x.mul * up + x.add * T(n - siz[x.to]);
      ans[x.to] = dn[x.to] + pc;
      self(self, x.to, u);
    }
  };
  df1(df1, 0, -1);
  return ans;
}

} // namespace noya
#ifndef NOYA_TREE_PATH_AFFINE_SUM_HPP
#define NOYA_TREE_PATH_AFFINE_SUM_HPP 1

/// @complexity Time: O(n).
/// Space: O(n), including O(n) recursion stack on a path.

#include <cassert>
#include <utility>
#include <vector>

namespace noya {

/// @brief Sum edge-affine path transforms from every possible root. For an
/// oriented edge x-y, the message sent by y is the sum of path values whose
/// destinations lie on y's side after deleting the edge. Applying bx+c to all
/// of them gives b times that message plus c times the side size. One postorder
/// pass builds child-side messages; one preorder reroot pass supplies the
/// complementary parent-side message.
template <class T>
std::vector<T> tree_path_affine_sums(const std::vector<T> &val,
                                     const std::vector<std::pair<int, int>> &ee,
                                     const std::vector<std::pair<T, T>> &aff) {
  int n = int(val.size());
  assert(ee.size() == aff.size());
  assert(int(ee.size()) == std::max(0, n - 1));
  if (n == 0) {
    return {};
  }
  struct edge {
    int to;
    T mul;
    T add;
  };
  std::vector<std::vector<edge>> g(n);
  for (int i = 0; i < n - 1; i++) {
    auto [a, b] = ee[i];
    auto [mul, add] = aff[i];
    g[a].push_back({b, mul, add});
    g[b].push_back({a, mul, add});
  }

  std::vector<int> siz(n);
  std::vector<T> dn(n);
  auto dfs = [&](auto &&self, int u, int fa) -> void {
    siz[u] = 1;
    dn[u] = val[u];
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      self(self, x.to, u);
      siz[u] += siz[x.to];
      dn[u] += x.mul * dn[x.to] + x.add * T(siz[x.to]);
    }
  };
  dfs(dfs, 0, -1);

  std::vector<T> ans(n);
  ans[0] = dn[0];
  auto df1 = [&](auto &&self, int u, int fa) -> void {
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      T cc = x.mul * dn[x.to] + x.add * T(siz[x.to]);
      T up = ans[u] - cc;
      T pc = x.mul * up + x.add * T(n - siz[x.to]);
      ans[x.to] = dn[x.to] + pc;
      self(self, x.to, u);
    }
  };
  df1(df1, 0, -1);
  return ans;
}

} // namespace noya

#endif // NOYA_TREE_PATH_AFFINE_SUM_HPP
#include <cassert>
#include <utility>
#include <vector>

/// @complexity Time: O(n).
/// Space: O(n), including O(n) recursion stack on a path.

namespace noya {

/// @brief Sum edge-affine path transforms from every possible root. For an
/// oriented edge x-y, the message sent by y is the sum of path values whose
/// destinations lie on y's side after deleting the edge. Applying bx+c to all
/// of them gives b times that message plus c times the side size. One postorder
/// pass builds child-side messages; one preorder reroot pass supplies the
/// complementary parent-side message.
template <class T>
std::vector<T> tree_path_affine_sums(const std::vector<T> &val,
                                     const std::vector<std::pair<int, int>> &ee,
                                     const std::vector<std::pair<T, T>> &aff) {
  int n = int(val.size());
  assert(ee.size() == aff.size());
  assert(int(ee.size()) == std::max(0, n - 1));
  if (n == 0) {
    return {};
  }
  struct edge {
    int to;
    T mul;
    T add;
  };
  std::vector<std::vector<edge>> g(n);
  for (int i = 0; i < n - 1; i++) {
    auto [a, b] = ee[i];
    auto [mul, add] = aff[i];
    g[a].push_back({b, mul, add});
    g[b].push_back({a, mul, add});
  }

  std::vector<int> siz(n);
  std::vector<T> dn(n);
  auto dfs = [&](auto &&self, int u, int fa) -> void {
    siz[u] = 1;
    dn[u] = val[u];
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      self(self, x.to, u);
      siz[u] += siz[x.to];
      dn[u] += x.mul * dn[x.to] + x.add * T(siz[x.to]);
    }
  };
  dfs(dfs, 0, -1);

  std::vector<T> ans(n);
  ans[0] = dn[0];
  auto df1 = [&](auto &&self, int u, int fa) -> void {
    for (const edge &x : g[u]) {
      if (x.to == fa) {
        continue;
      }
      T cc = x.mul * dn[x.to] + x.add * T(siz[x.to]);
      T up = ans[u] - cc;
      T pc = x.mul * up + x.add * T(n - siz[x.to]);
      ans[x.to] = dn[x.to] + pc;
      self(self, x.to, u);
    }
  };
  df1(df1, 0, -1);
  return ans;
}

} // namespace noya