cp-library

C++ Library for Competitive Programming

View the Project on GitHub emthrm/cp-library

:heavy_check_mark: グラフ/木/最小共通祖先 Euler tour technique 版
(test/graph/tree/lowest_common_ancestor_by_euler_tour.test.cpp)

Depends on

Code

/*
 * @title グラフ/木/最小共通祖先 Euler tour technique 版
 *
 * verification-helper: PROBLEM http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2667
 */

#include <iostream>
#include <vector>

#include "emthrm/data_structure/lazy_segment_tree.hpp"
#include "emthrm/graph/edge.hpp"
#include "emthrm/graph/tree/lowest_common_ancestor_by_euler_tour_technique.hpp"

int main() {
  int n, q;
  std::cin >> n >> q;
  std::vector<std::vector<emthrm::Edge<long long>>> graph(n);
  for (int i = 0; i < n - 1; ++i) {
    int a, b;
    std::cin >> a >> b;
    graph[a].emplace_back(a, b, 0);
    graph[b].emplace_back(b, a, 0);
  }
  emthrm::LowestCommonAncestor<long long> lowest_common_ancestor(graph, 0);
  struct M {
    using Monoid = struct { int num; long long sum; };
    using OperatorMonoid = int;
    static constexpr Monoid m_id() { return Monoid{0, 0}; }
    static constexpr OperatorMonoid o_id() { return 0; }
    static Monoid m_merge(const Monoid& a, const Monoid& b) {
      return Monoid{a.num + b.num, a.sum + b.sum};
    }
    static OperatorMonoid o_merge(const OperatorMonoid& a,
                                  const OperatorMonoid& b) {
      return a + b;
    }
    static Monoid apply(Monoid a, const OperatorMonoid& b) {
      a.sum += b * a.num;
      return a;
    }
  };
  std::vector<M::Monoid> init((n - 1) * 2, M::Monoid{0, 0});
  for (int i = 1; i < n; ++i) {
    init[lowest_common_ancestor.down[i]].num = 1;
    init[lowest_common_ancestor.up[i]].num = -1;
  }
  emthrm::LazySegmentTree<M> seg(init);
  const auto fn = [&seg](const int a, const int b) -> long long {
    return seg.get(a, b).sum;
  };
  while (q--) {
    int type;
    std::cin >> type;
    if (type == 0) {
      int u, v;
      std::cin >> u >> v;
      const int lca = lowest_common_ancestor.query(u, v);
      std::cout << lowest_common_ancestor.query_e<long long>(lca, u, fn)
                   + lowest_common_ancestor.query_e<long long>(lca, v, fn)
                << '\n';
    } else if (type == 1) {
      int v, x;
      std::cin >> v >> x;
      lowest_common_ancestor.update_subtree_e(
          v, [&seg, x](const int a, const int b) { seg.apply(a, b, x); });
    }
  }
  return 0;
}
#line 1 "test/graph/tree/lowest_common_ancestor_by_euler_tour.test.cpp"
/*
 * @title グラフ/木/最小共通祖先 Euler tour technique 版
 *
 * verification-helper: PROBLEM http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2667
 */

#include <iostream>
#include <vector>

#line 1 "include/emthrm/data_structure/lazy_segment_tree.hpp"



#include <algorithm>
#include <bit>
// #include <cassert>
#include <limits>
#include <type_traits>
#line 10 "include/emthrm/data_structure/lazy_segment_tree.hpp"

namespace emthrm {

template <typename T>
requires requires {
  typename T::Monoid;
  typename T::OperatorMonoid;
  {T::m_id()} -> std::same_as<typename T::Monoid>;
  {T::o_id()} -> std::same_as<typename T::OperatorMonoid>;
  {T::m_merge(std::declval<typename T::Monoid>(),
              std::declval<typename T::Monoid>())}
      -> std::same_as<typename T::Monoid>;
  {T::o_merge(std::declval<typename T::OperatorMonoid>(),
              std::declval<typename T::OperatorMonoid>())}
      -> std::same_as<typename T::OperatorMonoid>;
  {T::apply(std::declval<typename T::Monoid>(),
            std::declval<typename T::OperatorMonoid>())}
      -> std::same_as<typename T::Monoid>;
}
struct LazySegmentTree {
  using Monoid = typename T::Monoid;
  using OperatorMonoid = typename T::OperatorMonoid;

  explicit LazySegmentTree(const int n)
      : LazySegmentTree(std::vector<Monoid>(n, T::m_id())) {}

  explicit LazySegmentTree(const std::vector<Monoid>& a)
      : n(a.size()), height(std::countr_zero(std::bit_ceil(a.size()))),
        p2(1 << height) {
    lazy.assign(p2, T::o_id());
    data.assign(p2 << 1, T::m_id());
    std::copy(a.begin(), a.end(), data.begin() + p2);
    for (int i = p2 - 1; i > 0; --i) {
      data[i] = T::m_merge(data[i << 1], data[(i << 1) + 1]);
    }
  }

  void set(int idx, const Monoid val) {
    idx += p2;
    for (int i = height; i > 0; --i) {
      propagate(idx >> i);
    }
    data[idx] = val;
    for (int i = 1; i <= height; ++i) {
      const int current_idx = idx >> i;
      data[current_idx] =
          T::m_merge(data[current_idx << 1], data[(current_idx << 1) + 1]);
    }
  }

  void apply(int idx, const OperatorMonoid val) {
    idx += p2;
    for (int i = height; i > 0; --i) {
      propagate(idx >> i);
    }
    data[idx] = T::apply(data[idx], val);
    for (int i = 1; i <= height; ++i) {
      const int current_idx = idx >> i;
      data[current_idx] =
          T::m_merge(data[current_idx << 1], data[(current_idx << 1) + 1]);
    }
  }

  void apply(int left, int right, const OperatorMonoid val) {
    if (right <= left) [[unlikely]] return;
    left += p2;
    right += p2;
    const int ctz_left = std::countr_zero(static_cast<unsigned int>(left));
    for (int i = height; i > ctz_left; --i) {
      propagate(left >> i);
    }
    const int ctz_right = std::countr_zero(static_cast<unsigned int>(right));
    for (int i = height; i > ctz_right; --i) {
      propagate(right >> i);
    }
    for (int l = left, r = right; l < r; l >>= 1, r >>= 1) {
      if (l & 1) apply_sub(l++, val);
      if (r & 1) apply_sub(--r, val);
    }
    for (int i = left >> (ctz_left + 1); i > 0; i >>= 1) {
      data[i] = T::m_merge(data[i << 1], data[(i << 1) + 1]);
    }
    for (int i = right >> (ctz_right + 1); i > 0; i >>= 1) {
      data[i] = T::m_merge(data[i << 1], data[(i << 1) + 1]);
    }
  }

  Monoid get(int left, int right) {
    if (right <= left) [[unlikely]] return T::m_id();
    left += p2;
    right += p2;
    const int ctz_left = std::countr_zero(static_cast<unsigned int>(left));
    for (int i = height; i > ctz_left; --i) {
      propagate(left >> i);
    }
    const int ctz_right = std::countr_zero(static_cast<unsigned int>(right));
    for (int i = height; i > ctz_right; --i) {
      propagate(right >> i);
    }
    Monoid res_l = T::m_id(), res_r = T::m_id();
    for (; left < right; left >>= 1, right >>= 1) {
      if (left & 1) res_l = T::m_merge(res_l, data[left++]);
      if (right & 1) res_r = T::m_merge(data[--right], res_r);
    }
    return T::m_merge(res_l, res_r);
  }

  Monoid operator[](const int idx) {
    const int node = idx + p2;
    for (int i = height; i > 0; --i) {
      propagate(node >> i);
    }
    return data[node];
  }

  template <typename G>
  int find_right(int left, const G g) {
    if (left >= n) [[unlikely]] return n;
    left += p2;
    for (int i = height; i > 0; --i) {
      propagate(left >> i);
    }
    Monoid val = T::m_id();
    do {
      while (!(left & 1)) left >>= 1;
      Monoid nxt = T::m_merge(val, data[left]);
      if (!g(nxt)) {
        while (left < p2) {
          propagate(left);
          left <<= 1;
          nxt = T::m_merge(val, data[left]);
          if (g(nxt)) {
            val = nxt;
            ++left;
          }
        }
        return left - p2;
      }
      val = nxt;
      ++left;
    } while (!std::has_single_bit(static_cast<unsigned int>(left)));
    return n;
  }

  template <typename G>
  int find_left(int right, const G g) {
    if (right <= 0) [[unlikely]] return -1;
    right += p2;
    for (int i = height; i > 0; --i) {
      propagate((right - 1) >> i);
    }
    Monoid val = T::m_id();
    do {
      --right;
      while (right > 1 && (right & 1)) right >>= 1;
      Monoid nxt = T::m_merge(data[right], val);
      if (!g(nxt)) {
        while (right < p2) {
          propagate(right);
          right = (right << 1) + 1;
          nxt = T::m_merge(data[right], val);
          if (g(nxt)) {
            val = nxt;
            --right;
          }
        }
        return right - p2;
      }
      val = nxt;
    } while (!std::has_single_bit(static_cast<unsigned int>(right)));
    return -1;
  }

 private:
  const int n, height, p2;
  std::vector<Monoid> data;
  std::vector<OperatorMonoid> lazy;

  void apply_sub(const int idx, const OperatorMonoid& val) {
    data[idx] = T::apply(data[idx], val);
    if (idx < p2) lazy[idx] = T::o_merge(lazy[idx], val);
  }

  void propagate(const int idx) {
    // assert(1 <= idx && idx < p2);
    apply_sub(idx << 1, lazy[idx]);
    apply_sub((idx << 1) + 1, lazy[idx]);
    lazy[idx] = T::o_id();
  }
};

namespace monoid {

template <typename T>
struct RangeMinimumAndUpdateQuery {
  using Monoid = T;
  using OperatorMonoid = T;
  static constexpr Monoid m_id() { return std::numeric_limits<Monoid>::max(); }
  static constexpr OperatorMonoid o_id() {
    return std::numeric_limits<OperatorMonoid>::max();
  }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return std::min(a, b);
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return b == o_id() ? a : b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return b == o_id() ? a : b;
  }
};

template <typename T>
struct RangeMaximumAndUpdateQuery {
  using Monoid = T;
  using OperatorMonoid = T;
  static constexpr Monoid m_id() {
    return std::numeric_limits<Monoid>::lowest();
  }
  static constexpr OperatorMonoid o_id() {
    return std::numeric_limits<OperatorMonoid>::lowest();
  }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return std::max(a, b);
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return b == o_id() ? a : b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return b == o_id()? a : b;
  }
};

template <typename T, T Inf>
struct RangeMinimumAndAddQuery {
  using Monoid = T;
  using OperatorMonoid = T;
  static constexpr Monoid m_id() { return Inf; }
  static constexpr OperatorMonoid o_id() { return 0; }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return std::min(a, b);
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return a + b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return a + b;
  }
};

template <typename T, T Inf>
struct RangeMaximumAndAddQuery {
  using Monoid = T;
  using OperatorMonoid = T;
  static constexpr Monoid m_id() { return -Inf; }
  static constexpr OperatorMonoid o_id() { return 0; }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return std::max(a, b);
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return a + b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return a + b;
  }
};

template <typename T>
struct RangeSumAndUpdateQuery {
  using Monoid = struct { T sum; int len; };
  using OperatorMonoid = T;
  static std::vector<Monoid> init(const int n) {
    return std::vector<Monoid>(n, Monoid{0, 1});
  }
  static constexpr Monoid m_id() { return {0, 0}; }
  static constexpr OperatorMonoid o_id() {
    return std::numeric_limits<OperatorMonoid>::max();
  }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return Monoid{a.sum + b.sum, a.len + b.len};
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return b == o_id() ? a : b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return Monoid{b == o_id() ? a.sum : b * a.len, a.len};
  }
};

template <typename T>
struct RangeSumAndAddQuery {
  using Monoid = struct { T sum; int len; };
  using OperatorMonoid = T;
  static std::vector<Monoid> init(const int n) {
    return std::vector<Monoid>(n, Monoid{0, 1});
  }
  static constexpr Monoid m_id() { return {0, 0}; }
  static constexpr OperatorMonoid o_id() { return 0; }
  static Monoid m_merge(const Monoid& a, const Monoid& b) {
    return Monoid{a.sum + b.sum, a.len + b.len};
  }
  static OperatorMonoid o_merge(const OperatorMonoid& a,
                                const OperatorMonoid& b) {
    return a + b;
  }
  static Monoid apply(const Monoid& a, const OperatorMonoid& b) {
    return Monoid{a.sum + b * a.len, a.len};
  }
};

}  // namespace monoid

}  // namespace emthrm


#line 1 "include/emthrm/graph/edge.hpp"
/**
 * @title 辺
 */

#ifndef EMTHRM_GRAPH_EDGE_HPP_
#define EMTHRM_GRAPH_EDGE_HPP_

#include <compare>

namespace emthrm {

template <typename CostType>
struct Edge {
  CostType cost;
  int src, dst;

  explicit Edge(const int src, const int dst, const CostType cost = 0)
      : cost(cost), src(src), dst(dst) {}

  auto operator<=>(const Edge& x) const = default;
};

}  // namespace emthrm

#endif  // EMTHRM_GRAPH_EDGE_HPP_
#line 1 "include/emthrm/graph/tree/lowest_common_ancestor_by_euler_tour_technique.hpp"



#line 5 "include/emthrm/graph/tree/lowest_common_ancestor_by_euler_tour_technique.hpp"
#include <utility>
#line 7 "include/emthrm/graph/tree/lowest_common_ancestor_by_euler_tour_technique.hpp"

#line 1 "include/emthrm/data_structure/sparse_table.hpp"



#line 6 "include/emthrm/data_structure/sparse_table.hpp"
#include <cassert>
#include <functional>
#line 9 "include/emthrm/data_structure/sparse_table.hpp"

namespace emthrm {

template <typename Band>
struct SparseTable {
  using BinOp = std::function<Band(Band, Band)>;

  SparseTable() = default;

  explicit SparseTable(const std::vector<Band>& a, const BinOp bin_op) {
    init(a, bin_op);
  }

  void init(const std::vector<Band>& a, const BinOp bin_op_) {
    bin_op = bin_op_;
    const int n = a.size();
    assert(n > 0);
    lg.assign(n + 1, 0);
    for (int i = 2; i <= n; ++i) {
      lg[i] = lg[i >> 1] + 1;
    }
    const int table_h = std::countr_zero(std::bit_floor(a.size())) + 1;
    data.assign(table_h, std::vector<Band>(n));
    std::copy(a.begin(), a.end(), data.front().begin());
    for (int i = 1; i < table_h; ++i) {
      for (int j = 0; j + (1 << i) <= n; ++j) {
        data[i][j] = bin_op(data[i - 1][j], data[i - 1][j + (1 << (i - 1))]);
      }
    }
  }

  Band query(const int left, const int right) const {
    assert(left < right);
    const int h = lg[right - left];
    return bin_op(data[h][left], data[h][right - (1 << h)]);
  }

 private:
  BinOp bin_op;
  std::vector<int> lg;
  std::vector<std::vector<Band>> data;
};

}  // namespace emthrm


#line 1 "include/emthrm/graph/edge.hpp"
/**
 * @title 辺
 */

#ifndef EMTHRM_GRAPH_EDGE_HPP_
#define EMTHRM_GRAPH_EDGE_HPP_

#include <compare>

namespace emthrm {

template <typename CostType>
struct Edge {
  CostType cost;
  int src, dst;

  explicit Edge(const int src, const int dst, const CostType cost = 0)
      : cost(cost), src(src), dst(dst) {}

  auto operator<=>(const Edge& x) const = default;
};

}  // namespace emthrm

#endif  // EMTHRM_GRAPH_EDGE_HPP_
#line 1 "include/emthrm/graph/tree/euler_tour_technique.hpp"



#line 5 "include/emthrm/graph/tree/euler_tour_technique.hpp"

#line 1 "include/emthrm/graph/edge.hpp"
/**
 * @title 辺
 */

#ifndef EMTHRM_GRAPH_EDGE_HPP_
#define EMTHRM_GRAPH_EDGE_HPP_

#include <compare>

namespace emthrm {

template <typename CostType>
struct Edge {
  CostType cost;
  int src, dst;

  explicit Edge(const int src, const int dst, const CostType cost = 0)
      : cost(cost), src(src), dst(dst) {}

  auto operator<=>(const Edge& x) const = default;
};

}  // namespace emthrm

#endif  // EMTHRM_GRAPH_EDGE_HPP_
#line 7 "include/emthrm/graph/tree/euler_tour_technique.hpp"

namespace emthrm {

template <typename CostType>
struct EulerTourTechnique {
  std::vector<int> preorder, depth, left, right, down, up;
  std::vector<CostType> tour;

  explicit EulerTourTechnique(
      const std::vector<std::vector<Edge<CostType>>> &graph, const int root = 0)
      : graph(graph) {
    const int n = graph.size();
    left.resize(n);
    right.resize(n);
    down.assign(n, -1);
    up.assign(n, (n - 1) << 1);
    dfs(-1, root, 0);
  }

  template <typename Fn>
  void update_v(const int ver, const Fn f) const {
    f(left[ver], right[ver] + 1);
  }

  template <typename T, typename Fn>
  T query_v(const int ver, const Fn f) const {
    return f(left[ver], right[ver] + 1);
  }

  template <typename T, typename Fn>
  T query_e(const int u, const int v, const Fn f) const {
    return f(down[u] + 1, down[v] + 1);
  }

  template <typename Fn>
  void update_subtree_e(const int ver, const Fn f) const {
    f(down[ver] + 1, up[ver]);
  }

  template <typename T, typename Fn>
  T query_subtree_e(const int ver, const Fn f) const {
    return f(down[ver] + 1, up[ver]);
  }

 private:
  const std::vector<std::vector<Edge<CostType>>> graph;

  void dfs(const int par, const int ver, const int cur_depth) {
    left[ver] = preorder.size();
    preorder.emplace_back(ver);
    depth.emplace_back(cur_depth);
    for (const Edge<CostType>& e : graph[ver]) {
      if (e.dst != par) {
        down[e.dst] = tour.size();
        tour.emplace_back(e.cost);
        dfs(ver, e.dst, cur_depth + 1);
        preorder.emplace_back(ver);
        depth.emplace_back(cur_depth);
        up[e.dst] = tour.size();
        tour.emplace_back(-e.cost);
      }
    }
    right[ver] = preorder.size() - 1;
  }
};

}  // namespace emthrm


#line 11 "include/emthrm/graph/tree/lowest_common_ancestor_by_euler_tour_technique.hpp"

namespace emthrm {

template <typename CostType>
struct LowestCommonAncestor : EulerTourTechnique<CostType> {
  explicit LowestCommonAncestor(
      const std::vector<std::vector<Edge<CostType>>>& graph,
      const int root = 0)
      : EulerTourTechnique<CostType>(graph, root) {
    const int n = this->preorder.size();
    std::vector<std::pair<int, int>> nodes(n);
    for (int i = 0; i < n; ++i) {
      nodes[i] = {this->depth[i], this->preorder[i]};
    }
    sparse_table.init(
        nodes,
        [](const std::pair<int, int>& a, const std::pair<int, int>& b)
            -> std::pair<int, int> {
          return std::min(a, b);
        });
  }

  int query(int u, int v) const {
    u = this->left[u];
    v = this->left[v];
    if (u > v) std::swap(u, v);
    return sparse_table.query(u, v + 1).second;
  }

 private:
  SparseTable<std::pair<int, int>> sparse_table;
};

}  // namespace emthrm


#line 13 "test/graph/tree/lowest_common_ancestor_by_euler_tour.test.cpp"

int main() {
  int n, q;
  std::cin >> n >> q;
  std::vector<std::vector<emthrm::Edge<long long>>> graph(n);
  for (int i = 0; i < n - 1; ++i) {
    int a, b;
    std::cin >> a >> b;
    graph[a].emplace_back(a, b, 0);
    graph[b].emplace_back(b, a, 0);
  }
  emthrm::LowestCommonAncestor<long long> lowest_common_ancestor(graph, 0);
  struct M {
    using Monoid = struct { int num; long long sum; };
    using OperatorMonoid = int;
    static constexpr Monoid m_id() { return Monoid{0, 0}; }
    static constexpr OperatorMonoid o_id() { return 0; }
    static Monoid m_merge(const Monoid& a, const Monoid& b) {
      return Monoid{a.num + b.num, a.sum + b.sum};
    }
    static OperatorMonoid o_merge(const OperatorMonoid& a,
                                  const OperatorMonoid& b) {
      return a + b;
    }
    static Monoid apply(Monoid a, const OperatorMonoid& b) {
      a.sum += b * a.num;
      return a;
    }
  };
  std::vector<M::Monoid> init((n - 1) * 2, M::Monoid{0, 0});
  for (int i = 1; i < n; ++i) {
    init[lowest_common_ancestor.down[i]].num = 1;
    init[lowest_common_ancestor.up[i]].num = -1;
  }
  emthrm::LazySegmentTree<M> seg(init);
  const auto fn = [&seg](const int a, const int b) -> long long {
    return seg.get(a, b).sum;
  };
  while (q--) {
    int type;
    std::cin >> type;
    if (type == 0) {
      int u, v;
      std::cin >> u >> v;
      const int lca = lowest_common_ancestor.query(u, v);
      std::cout << lowest_common_ancestor.query_e<long long>(lca, u, fn)
                   + lowest_common_ancestor.query_e<long long>(lca, v, fn)
                << '\n';
    } else if (type == 1) {
      int v, x;
      std::cin >> v >> x;
      lowest_common_ancestor.update_subtree_e(
          v, [&seg, x](const int a, const int b) { seg.apply(a, b, x); });
    }
  }
  return 0;
}
Back to top page