Library

This documentation is automatically generated by online-judge-tools/verification-helper

View the Project on GitHub jellc/Library

:heavy_check_mark: test/aizu-online-judge/2450.test.cpp

Depends on

Code

#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2450"

#include <algorithm>
#include <iostream>

#include "src/data_structure/segment_tree/lazy.hpp"
#include "src/graph/undirected/tree/heavy_light_decomposition.hpp"
#include "src/utils/io/istream.hpp"

int main() {
  struct endo {
    bool assign = false;
    int value;
    endo() = default;
    endo(int v) : assign(true), value(v) {}
    endo operator*(endo rhs) const {
      if (rhs.assign) return rhs;
      return *this;
    }
  };
  struct mono {
    int cnt = 0;
    int sum = 0;
    int left = 0;
    int right = 0;
    int max = 0;
    int best = -10000;
    mono operator+(mono rhs) const {
      return {cnt + rhs.cnt,
              sum + rhs.sum,
              std::max(left, sum + rhs.left),
              std::max(right + rhs.sum, rhs.right),
              std::max({max, rhs.max, right + rhs.left}),
              std::max(best, rhs.best)};
    }
    mono operator*(endo rhs) const {
      mono ret = *this;
      if (rhs.assign) {
        if (rhs.value < 0) {
          ret.sum = rhs.value * cnt;
          ret.left = ret.right = ret.max = 0;
        } else {
          ret.sum = ret.left = ret.right = ret.max = cnt * rhs.value;
        }
        ret.best = rhs.value;
      }
      return ret;
    }
  };

  int n, q;
  std::cin >> n >> q;
  std::vector<int> w(n);
  workspace::cin >> w;
  heavy_light_decomposition hld(n);
  for (auto e = 1; e != n; ++e) {
    int u, v;
    std::cin >> u >> v;
    --u, --v;
    hld.add_edge(u, v);
  }
  hld.make(0);
  workspace::lazy_segment_tree<mono, endo> seg(n);
  for (auto v = 0; v != n; ++v) {
    auto &now = seg[hld.index(v)];
    now.cnt = 1;
    now = now * endo{w[v]};
  }
  while (q--) {
    int tp, a, b, c;
    std::cin >> tp >> a >> b >> c;
    --a, --b;
    auto [left, right] = hld.split_path(a, b);
    if (tp == 1) {
      for (auto &&[l, r] : left) {
        seg.apply(l, r, c);
      }
      for (auto &&[l, r] : right) {
        seg.apply(l, r, c);
      }
    } else {
      mono lv;
      for (auto &&[l, r] : left) {
        lv = seg.fold(l, r) + lv;
      }
      mono rv;
      for (auto &&[l, r] : right) {
        rv = seg.fold(l, r) + rv;
      }
      std::swap(lv.left, lv.right);
      auto all = lv + rv;
      if (all.max)
        std::cout << all.max << '\n';
      else
        std::cout << all.best << '\n';
    }
  }
}
#line 1 "test/aizu-online-judge/2450.test.cpp"
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/problems/2450"

#include <algorithm>
#include <iostream>

#line 2 "src/data_structure/segment_tree/lazy.hpp"

/**
 * @file lazy.hpp
 * @brief Lazy Segment Tree
 */

#include <cassert>
#include <queue>
#include <vector>

#line 2 "src/algebra/system/monoid.hpp"
#include <limits>

namespace workspace {
template <class T, class E = T> struct min_monoid {
  using value_type = T;
  static T min, max;
  T value;
  min_monoid() : value(max) {}
  min_monoid(const T &value) : value(value) {}
  operator T() const { return value; }
  min_monoid operator+(const min_monoid &rhs) const {
    return value < rhs.value ? *this : rhs;
  }
  min_monoid operator*(const E &rhs) const;
};

template <class T, class E>
T min_monoid<T, E>::min = std::numeric_limits<T>::min() / 2;
template <class T, class E>
T min_monoid<T, E>::max = std::numeric_limits<T>::max() / 2;

template <class T, class E = T> struct max_monoid : min_monoid<T, E> {
  using base = min_monoid<T, E>;
  using base::min_monoid;
  max_monoid() : base(base::min) {}
  max_monoid operator+(const max_monoid &rhs) const {
    return !(base::value < rhs.value) ? *this : rhs;
  }
  max_monoid operator*(const E &rhs) const;
};
}
#line 2 "src/algebra/system/operation.hpp"

/**
 * @file operation.hpp
 * @brief Operation Traits
 */

#include <functional>
#include <type_traits>

#line 2 "lib/cxx17"

#line 2 "lib/cxx14"

#ifndef _CXX14_CONSTEXPR
#if __cplusplus >= 201402L
#define _CXX14_CONSTEXPR constexpr
#else
#define _CXX14_CONSTEXPR
#endif
#endif
#line 4 "lib/cxx17"

#ifndef _CXX17_CONSTEXPR
#if __cplusplus >= 201703L
#define _CXX17_CONSTEXPR constexpr
#else
#define _CXX17_CONSTEXPR
#endif
#endif

#ifndef _CXX17_STATIC_ASSERT
#if __cplusplus >= 201703L
#define _CXX17_STATIC_ASSERT static_assert
#else
#define _CXX17_STATIC_ASSERT assert
#endif
#endif

#include <iterator>

#if __cplusplus < 201703L

namespace std {

/**
 *  @brief  Return the size of a container.
 *  @param  __cont  Container.
 */
template <typename _Container>
constexpr auto size(const _Container& __cont) noexcept(noexcept(__cont.size()))
    -> decltype(__cont.size()) {
  return __cont.size();
}

/**
 *  @brief  Return the size of an array.
 */
template <typename _Tp, size_t _Nm>
constexpr size_t size(const _Tp (&)[_Nm]) noexcept {
  return _Nm;
}

/**
 *  @brief  Return whether a container is empty.
 *  @param  __cont  Container.
 */
template <typename _Container>
[[nodiscard]] constexpr auto empty(const _Container& __cont) noexcept(
    noexcept(__cont.empty())) -> decltype(__cont.empty()) {
  return __cont.empty();
}

/**
 *  @brief  Return whether an array is empty (always false).
 */
template <typename _Tp, size_t _Nm>
[[nodiscard]] constexpr bool empty(const _Tp (&)[_Nm]) noexcept {
  return false;
}

/**
 *  @brief  Return whether an initializer_list is empty.
 *  @param  __il  Initializer list.
 */
template <typename _Tp>
[[nodiscard]] constexpr bool empty(initializer_list<_Tp> __il) noexcept {
  return __il.size() == 0;
}

struct monostate {};

}  // namespace std

#else

#include <variant>

#endif
#line 12 "src/algebra/system/operation.hpp"

namespace workspace {

// Unary `+`
template <class _Tp>
using require_unary_plus = std::enable_if_t<
    std::is_convertible<decltype(+std::declval<const _Tp &>()), _Tp>::value>;

template <class _Tp, class = void> struct has_unary_plus : std::false_type {};

template <class _Tp>
struct has_unary_plus<_Tp, require_unary_plus<_Tp>> : std::true_type {};

// Unary `-`
template <class _Tp>
using require_unary_minus = std::enable_if_t<
    std::is_convertible<decltype(-std::declval<const _Tp &>()), _Tp>::value>;

template <class _Tp, class = void> struct has_unary_minus : std::false_type {};

template <class _Tp>
struct has_unary_minus<_Tp, require_unary_minus<_Tp>> : std::true_type {};

// Binary `+`
template <class _Tp1, class _Tp2 = _Tp1>
using require_binary_plus =
    std::enable_if_t<std::is_convertible<decltype(std::declval<const _Tp1 &>() +
                                                  std::declval<const _Tp2 &>()),
                                         _Tp1>::value>;

template <class _Tp1, class _Tp2 = _Tp1, class = void>
struct has_binary_plus : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_binary_plus<_Tp1, _Tp2, require_binary_plus<_Tp1, _Tp2>>
    : std::true_type {};

// Binary `-`
template <class _Tp1, class _Tp2 = _Tp1>
using require_binary_minus =
    std::__void_t<decltype(std::declval<const _Tp1 &>() -
                           std::declval<const _Tp2 &>())>;

template <class _Tp1, class _Tp2 = _Tp1, class = void>
struct has_binary_minus : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_binary_minus<_Tp1, _Tp2, require_binary_minus<_Tp1, _Tp2>>
    : std::true_type {};

// Binary `*`
template <class _Tp1, class _Tp2 = _Tp1>
using require_binary_multiplies =
    std::enable_if_t<std::is_convertible<decltype(std::declval<const _Tp1 &>() *
                                                  std::declval<const _Tp2 &>()),
                                         _Tp1>::value>;

template <class _Tp1, class _Tp2 = _Tp1, class = void>
struct has_binary_multiplies : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_binary_multiplies<_Tp1, _Tp2, require_binary_multiplies<_Tp1, _Tp2>>
    : std::true_type {};

// Binary `/`
template <class _Tp1, class _Tp2 = _Tp1>
using require_binary_divides =
    std::enable_if_t<std::is_convertible<decltype(std::declval<const _Tp1 &>() /
                                                  std::declval<const _Tp2 &>()),
                                         _Tp1>::value>;

template <class _Tp1, class _Tp2 = _Tp1, class = void>
struct has_binary_divides : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_binary_divides<_Tp1, _Tp2, require_binary_divides<_Tp1, _Tp2>>
    : std::true_type {};

// Binary `%`
template <class _Tp1, class _Tp2 = _Tp1>
using require_binary_modulus =
    std::enable_if_t<std::is_convertible<decltype(std::declval<const _Tp1 &>() %
                                                  std::declval<const _Tp2 &>()),
                                         _Tp1>::value>;

template <class _Tp1, class _Tp2 = _Tp1, class = void>
struct has_binary_modulus : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_binary_modulus<_Tp1, _Tp2, require_binary_modulus<_Tp1, _Tp2>>
    : std::true_type {};

template <class _Tp1, class _Tp2 = _Tp1, class = void, class = void,
          class = void, class = void>
struct has_arithmetic : std::false_type {};

template <class _Tp1, class _Tp2>
struct has_arithmetic<_Tp1, _Tp2, require_binary_plus<_Tp1, _Tp2>,
                      require_binary_minus<_Tp1, _Tp2>,
                      require_binary_multiplies<_Tp1, _Tp2>,
                      require_binary_divides<_Tp1, _Tp2>> : std::true_type {};

template <class _Tp1, class _Tp2 = _Tp1>
using require_arithmetic = std::enable_if_t<has_arithmetic<_Tp1, _Tp2>::value>;

// Binary `<`
template <class _Tp, class = void> struct is_comparable : std::false_type {};

template <class _Tp>
struct is_comparable<_Tp, std::__void_t<decltype(std::declval<const _Tp &>() <
                                                 std::declval<const _Tp &>())>>
    : std::true_type {};

template <class _Tp, bool _Default = false> struct try_less : std::less<_Tp> {
  constexpr bool operator()(const _Tp &__x, const _Tp &__y) noexcept {
    if _CXX17_CONSTEXPR (is_comparable<_Tp>::value)
      return std::less<_Tp>::operator()(__x, __y);
    else
      return _Default;
  }
};

}  // namespace workspace
#line 2 "src/utils/sfinae.hpp"

/**
 * @file sfinae.hpp
 * @brief SFINAE
 */

#include <cstdint>
#line 11 "src/utils/sfinae.hpp"

#ifndef __INT128_DEFINED__

#ifdef __SIZEOF_INT128__
#define __INT128_DEFINED__ 1
#else
#define __INT128_DEFINED__ 0
#endif

#endif

namespace std {

#if __INT128_DEFINED__

template <> struct make_signed<__uint128_t> { using type = __int128_t; };
template <> struct make_signed<__int128_t> { using type = __int128_t; };

template <> struct make_unsigned<__uint128_t> { using type = __uint128_t; };
template <> struct make_unsigned<__int128_t> { using type = __uint128_t; };

template <> struct is_signed<__uint128_t> : std::false_type {};
template <> struct is_signed<__int128_t> : std::true_type {};

template <> struct is_unsigned<__uint128_t> : std::true_type {};
template <> struct is_unsigned<__int128_t> : std::false_type {};

#endif

}  // namespace std

namespace workspace {

template <class Tp, class... Args> struct variadic_front { using type = Tp; };

template <class... Args> struct variadic_back;

template <class Tp> struct variadic_back<Tp> { using type = Tp; };

template <class Tp, class... Args> struct variadic_back<Tp, Args...> {
  using type = typename variadic_back<Args...>::type;
};

template <class type, template <class> class trait>
using enable_if_trait_type = typename std::enable_if<trait<type>::value>::type;

/**
 * @brief Return type of subscripting ( @c [] ) access.
 */
template <class _Tp>
using subscripted_type =
    typename std::decay<decltype(std::declval<_Tp&>()[0])>::type;

template <class Container>
using element_type = typename std::decay<decltype(*std::begin(
    std::declval<Container&>()))>::type;

template <class _Tp, class = void> struct has_begin : std::false_type {};

template <class _Tp>
struct has_begin<
    _Tp, std::__void_t<decltype(std::begin(std::declval<const _Tp&>()))>>
    : std::true_type {
  using type = decltype(std::begin(std::declval<const _Tp&>()));
};

template <class _Tp, class = void> struct has_size : std::false_type {};

template <class _Tp>
struct has_size<_Tp, std::__void_t<decltype(std::size(std::declval<_Tp>()))>>
    : std::true_type {};

template <class _Tp, class = void> struct has_resize : std::false_type {};

template <class _Tp>
struct has_resize<_Tp, std::__void_t<decltype(std::declval<_Tp>().resize(
                           std::declval<size_t>()))>> : std::true_type {};

template <class _Tp, class = void> struct has_mod : std::false_type {};

template <class _Tp>
struct has_mod<_Tp, std::__void_t<decltype(_Tp::mod)>> : std::true_type {};

template <class _Tp, class = void> struct is_integral_ext : std::false_type {};
template <class _Tp>
struct is_integral_ext<
    _Tp, typename std::enable_if<std::is_integral<_Tp>::value>::type>
    : std::true_type {};

#if __INT128_DEFINED__

template <> struct is_integral_ext<__int128_t> : std::true_type {};
template <> struct is_integral_ext<__uint128_t> : std::true_type {};

#endif

#if __cplusplus >= 201402

template <class _Tp>
constexpr static bool is_integral_ext_v = is_integral_ext<_Tp>::value;

#endif

template <typename _Tp, typename = void> struct multiplicable_uint {
  using type = uint_least32_t;
};
template <typename _Tp>
struct multiplicable_uint<
    _Tp,
    typename std::enable_if<(2 < sizeof(_Tp)) &&
                            (!__INT128_DEFINED__ || sizeof(_Tp) <= 4)>::type> {
  using type = uint_least64_t;
};

#if __INT128_DEFINED__

template <typename _Tp>
struct multiplicable_uint<_Tp,
                          typename std::enable_if<(4 < sizeof(_Tp))>::type> {
  using type = __uint128_t;
};

#endif

template <typename _Tp> struct multiplicable_int {
  using type =
      typename std::make_signed<typename multiplicable_uint<_Tp>::type>::type;
};

template <typename _Tp> struct multiplicable {
  using type = std::conditional_t<
      is_integral_ext<_Tp>::value,
      std::conditional_t<std::is_signed<_Tp>::value,
                         typename multiplicable_int<_Tp>::type,
                         typename multiplicable_uint<_Tp>::type>,
      _Tp>;
};

template <class> struct first_arg { using type = void; };

template <class _R, class _Tp, class... _Args>
struct first_arg<_R(_Tp, _Args...)> {
  using type = _Tp;
};

template <class _R, class _Tp, class... _Args>
struct first_arg<_R (*)(_Tp, _Args...)> {
  using type = _Tp;
};

template <class _G, class _R, class _Tp, class... _Args>
struct first_arg<_R (_G::*)(_Tp, _Args...)> {
  using type = _Tp;
};

template <class _G, class _R, class _Tp, class... _Args>
struct first_arg<_R (_G::*)(_Tp, _Args...) const> {
  using type = _Tp;
};

template <class _Tp, class = void> struct parse_compare : first_arg<_Tp> {};

template <class _Tp>
struct parse_compare<_Tp, std::__void_t<decltype(&_Tp::operator())>>
    : first_arg<decltype(&_Tp::operator())> {};

template <class _Container, class = void> struct get_dimension {
  static constexpr size_t value = 0;
};

template <class _Container>
struct get_dimension<_Container,
                     std::enable_if_t<has_begin<_Container>::value>> {
  static constexpr size_t value =
      1 + get_dimension<typename std::iterator_traits<
              typename has_begin<_Container>::type>::value_type>::value;
};

}  // namespace workspace
#line 2 "src/data_structure/segment_tree/waitings.hpp"

#line 5 "src/data_structure/segment_tree/waitings.hpp"

namespace workspace {

namespace internal {

struct waitings : std::queue<size_t> {
  waitings(size_t n) : in(n) {}

  bool push(size_t index) {
    // assert(index < in.size());
    if (in[index]) return false;
    emplace(index);
    return (in[index] = true);
  }

  size_t pop() {
    // assert(!empty());
    auto index = front();
    std::queue<size_t>::pop();
    in[index] = false;
    return index;
  }

 private:
  std::vector<int_least8_t> in;
};

}  // namespace internal

}  // namespace workspace
#line 16 "src/data_structure/segment_tree/lazy.hpp"

namespace workspace {

template <class _Monoid, class _End,
          class Monoid_container = std::vector<_Monoid>,
          class Endomorphism_container = std::vector<_End>>
class lazy_segment_tree {
  static_assert(
      std::is_same<_Monoid, typename Monoid_container::value_type>::value);

  static_assert(
      std::is_same<_End, typename Endomorphism_container::value_type>::value);

  static_assert(has_binary_plus<_Monoid>::value,
                "\'_Monoid\' has no proper binary \'operator+\'.");

  static_assert(has_binary_multiplies<_End>::value,
                "\'_End\' has no proper binary \'operator*\'.");

  static_assert(has_binary_multiplies<_Monoid, _End>::value,
                "\'_End\' is not applicable to \'_Monoid\'.");

  size_t size_orig, height, size_ext;
  Monoid_container data;
  Endomorphism_container lazy;
  internal::waitings wait;

  void repair() {
    while (!wait.empty()) {
      const size_t __i = wait.pop() >> 1;
      if (__i && wait.push(__i)) pull(__i);
    }
  }

  void _apply(size_t node, const _End &__e) {
    data[node] = data[node] * __e;
    if (node < size_ext) lazy[node] = lazy[node] * __e;
  }

  void push(size_t node) {
    _apply(node << 1, lazy[node]);
    _apply(node << 1 | 1, lazy[node]);
    lazy[node] = _End{};
  }

  void pull(size_t node) { data[node] = data[node << 1] + data[node << 1 | 1]; }

  template <class _Pred>
  size_t left_partition_subtree(size_t node, _Monoid mono, _Pred __pred) {
    assert(node);
    while (node < size_ext) {
      push(node);
      const _Monoid __tmp = data[(node <<= 1) | 1] + mono;
      if (__pred(__tmp))
        mono = std::move(__tmp);
      else
        ++node;
    }
    return ++node -= size_ext;
  }

  template <class _Pred>
  size_t right_partition_subtree(size_t node, _Monoid mono, _Pred __pred) {
    assert(node);
    while (node < size_ext) {
      push(node);
      const _Monoid __tmp = mono + data[node <<= 1];
      if (__pred(__tmp)) ++node, mono = std::move(__tmp);
    }
    return (node -= size_ext) < size_orig ? node : size_orig;
  }

 public:
  class iterator {
    lazy_segment_tree *__p;
    size_t __i;

   public:
    using difference_type = typename std::make_signed<size_t>::type;
    using value_type = _Monoid;
    using reference = _Monoid &;
    using pointer = iterator;
    using iterator_category = std::random_access_iterator_tag;

    /**
     * @brief Construct a new iterator object
     *
     */
    iterator() = default;

    /**
     * @brief Construct a new iterator object
     *
     * @param __p Pointer to a segment tree object
     * @param __i Index
     */
    iterator(lazy_segment_tree *__p, size_t __i) : __p(__p), __i(__i) {}

    bool operator==(iterator const &rhs) const {
      return __p == rhs.__p && __i == rhs.__i;
    }
    bool operator!=(iterator const &rhs) const { return !operator==(rhs); }

    bool operator<(iterator const &rhs) const { return __i < rhs.__i; }
    bool operator>(iterator const &rhs) const { return __i > rhs.__i; }
    bool operator<=(iterator const &rhs) const { return __i <= rhs.__i; }
    bool operator>=(iterator const &rhs) const { return __i >= rhs.__i; }

    iterator &operator++() { return ++__i, *this; }
    iterator &operator--() { return --__i, *this; }

    difference_type operator-(iterator const &rhs) const {
      return __i - rhs.__i;
    }

    /**
     * @brief
     *
     * @return reference
     */
    reference operator*() const { return __p->operator[](__i); }
  };

  using value_type = typename iterator::value_type;
  using reference = typename iterator::reference;

  iterator begin() { return {this, 0}; }
  iterator end() { return {this, size_orig}; }

  auto rbegin() { return std::make_reverse_iterator(end()); }
  auto rend() { return std::make_reverse_iterator(begin()); }

  lazy_segment_tree(size_t __n = 0)
      : size_orig{__n},
        height(__n > 1 ? 32 - __builtin_clz(__n - 1) : 0),
        size_ext{1u << height},
        data(size_ext << 1),
        lazy(size_ext),
        wait(size_ext << 1) {}

  lazy_segment_tree(size_t __n, const _Monoid &init) : lazy_segment_tree(__n) {
    std::fill_n(std::next(std::begin(data), size_ext), __n, init);
    for (size_t i{size_ext}; --i;) pull(i);
  }

  template <class iter_type, class value_type = typename std::iterator_traits<
                                 iter_type>::value_type>
  lazy_segment_tree(iter_type first, iter_type last)
      : size_orig(std::distance(first, last)),
        height(size_orig > 1 ? 32 - __builtin_clz(size_orig - 1) : 0),
        size_ext{1u << height},
        data(size_ext << 1),
        lazy(size_ext),
        wait(size_ext << 1) {
    static_assert(std::is_constructible<_Monoid, value_type>::value,
                  "_Monoid(iter_type::value_type) is not constructible.");
    for (auto iter{std::next(std::begin(data), size_ext)};
         iter != std::end(data) && first != last; ++iter, ++first)
      *iter = _Monoid(*first);
    for (size_t i{size_ext}; --i;) pull(i);
  }

  /**
   * @return Number of elements.
   */
  size_t size() const { return size_orig; }

  /**
   * @param __i Index of the element
   * @return Reference to the element.
   */
  _Monoid &operator[](size_t __i) {
    assert(__i < size_orig);
    __i |= size_ext;
    wait.push(__i);
    for (size_t i = height; i; --i) push(__i >> i);
    return data[__i];
  }

  void apply(const _End &__e) { apply(0, size_orig, __e); }

  void apply(size_t __i, const _End &__e) { apply(__i, __i + 1, __e); }

  void apply(size_t first, size_t last, const _End &__e) {
    assert(last <= size_orig);
    repair();
    if (first >= last) return;
    first += size_ext, last += size_ext;
    --last;
    for (size_t i = height; i; --i) push(first >> i), push(last >> i);
    ++last;
    for (size_t l = first, r = last; l != r; l >>= 1, r >>= 1) {
      if (l & 1) _apply(l++, __e);
      if (r & 1) _apply(--r, __e);
    }
    for (first >>= __builtin_ffs(first); first; first >>= 1) pull(first);
    for (last >>= __builtin_ffs(last); last; last >>= 1) pull(last);
  }

  /**
   * @param first Left end, inclusive
   * @param last Right end, exclusive
   * @return Sum of elements in the interval.
   */
  _Monoid fold(size_t first, size_t last) {
    assert(last <= size_orig);
    repair();
    if (first >= last) return _Monoid{};
    first += size_ext, last += size_ext - 1;
    _Monoid left_val{}, right_val{};
    for (size_t l = first, r = last + 1; l != r; l >>= 1, r >>= 1) {
      if (l & 1) left_val = left_val + data[l++];
      if (r & 1) right_val = data[--r] + right_val;
      left_val = left_val * lazy[first >>= 1];
      right_val = right_val * lazy[last >>= 1];
    }
    while (first >>= 1, last >>= 1) {
      left_val = left_val * lazy[first];
      right_val = right_val * lazy[last];
    }
    return left_val + right_val;
  }

  /**
   * @return Sum of all elements.
   */
  _Monoid fold() {
    repair();
    return data[1];
  }

  /**
   * @brief Binary search for the partition point.
   * @param __r Right fixed end of the interval, exclusive
   * @param __pred Predicate in the form of 'bool(_Monoid)'
   * @return Left end of the extremal interval satisfying the condition,
   * inclusive.
   */
  template <class _Pred> size_t left_partition(size_t __r, _Pred __pred) {
    assert(__r <= size_orig);
    repair();
    __r += size_ext - 1;
    for (size_t i{height}; i; --i) push(__r >> i);
    ++__r;
    _Monoid mono{};
    for (size_t __l{size_ext}, step{}; __l != __r;
         __l >>= 1, __r >>= 1, ++step) {
      if ((__l & 1) != (__r & 1)) {
        const _Monoid __tmp = data[--__r] + mono;
        if (!__pred(__tmp))
          return left_partition_subtree(__r, std::move(mono), __pred);
        mono = std::move(__tmp);
      }
    }
    return 0;
  }

  /**
   * @brief Binary search for the partition point.
   * @param __l Left fixed end of the interval, inclusive
   * @param __pred Predicate in the form of 'bool(_Monoid)'
   * @return Right end of the extremal interval satisfying the condition,
   * exclusive.
   */
  template <class _Pred> size_t right_partition(size_t __l, _Pred __pred) {
    assert(__l <= size_orig);
    repair();
    __l += size_ext;
    for (size_t i{height}; i; --i) push(__l >> i);
    _Monoid mono{};
    for (size_t __r{size_ext << 1}, step{}; __l != __r;
         __l >>= 1, __r >>= 1, ++step) {
      if ((__l & 1) != (__r & 1)) {
        const _Monoid __tmp = mono + data[__l];
        if (!__pred(__tmp))
          return right_partition_subtree(__l, std::move(mono), __pred);
        mono = std::move(__tmp);
        ++__l;
      }
    }
    return size_orig;
  }
};

}  // namespace workspace
#line 2 "src/graph/undirected/tree/heavy_light_decomposition.hpp"

/**
 * @file heavy_light_decomposition.hpp
 * @brief Heavy-Light Decomposition
 */

#line 9 "src/graph/undirected/tree/heavy_light_decomposition.hpp"
#include <numeric>
#line 11 "src/graph/undirected/tree/heavy_light_decomposition.hpp"

class heavy_light_decomposition {
  constexpr static size_t __nil = -1;

  std::vector<std::vector<size_t>> __tree;
  std::vector<size_t> __sorted, __in, __out, __head, __depth;

  size_t sort_children(size_t node, size_t prev) {
    size_t sum = 1, max_size = 0;

    for (size_t &to : __tree[node]) {
      if (to == prev) continue;
      __depth[to] = __depth[node] + 1;
      size_t child_size = sort_children(to, node);
      sum += child_size;
      if (max_size < child_size) {
        max_size = child_size;
        std::swap(__tree[node].front(), to);
      }
    }

    return sum;
  }

  void traverse(size_t node, size_t prev) {
    __in[node] = __sorted.size();
    __sorted.emplace_back(node);

    if (!__tree[node].empty() && __tree[node].front() != prev) {
      for (const size_t to : __tree[node])
        if (to != prev) __head[to] = node + size();
      __head[__tree[node].front()] =
          __head[node] < size() ? __head[node] : node;
      for (const size_t to : __tree[node])
        if (to != prev) traverse(to, node);
    }

    __out[node] = __sorted.size();
  }

  bool made() const { return !__sorted.empty(); }

 public:
  using interval = std::pair<size_t, size_t>;

  heavy_light_decomposition() = default;

  heavy_light_decomposition(size_t __n) : __tree(__n) {}

  /**
   * @return The size of the __tree.
   */
  size_t size() const { return __tree.size(); }

  /**
   * @param node The root of the subtree
   * @return The size of the subtree.
   */
  size_t size(size_t node) const {
    assert(made());
    return __out[node] - __in[node];
  }

  void add_edge(size_t __u, size_t __v) {
    assert(__u < size());
    assert(__v < size());
    __tree[__u].emplace_back(__v);
    __tree[__v].emplace_back(__u);
  }

  const decltype(__tree) &tree() const { return __tree; }

  /**
   * @brief Run HLD with given root __in linear time.
   * @param root The root node.
   */
  void make(size_t __root) {
    __sorted.clear(), __in.resize(size()), __out.resize(size()),
        __head.resize(size()), __depth.resize(size());
    __head[__root] = __root + size(), __depth[__root] = 0;
    sort_children(__root, __nil);
    traverse(__root, __root);
  }

  size_t prev_node(size_t node) const {
    assert(made());
    return __in[node] ? __sorted[__in[node] - 1] : __nil;
  }

  size_t next_node(size_t node) const {
    assert(made());
    return __in[node] + 1 < size() ? __sorted[__in[node] + 1] : __nil;
  }

  size_t index(size_t node) const {
    assert(made());
    return __in[node];
  }

  size_t node(size_t __i) const {
    assert(made());
    return __sorted[__i];
  }

  /**
   * @return The current root of the __tree.
   */
  size_t root() const {
    assert(made());
    return __sorted.front();
  }

  /**
   * @param root The root of the subtree.
   * @return The interval representing the subtree.
   */
  interval subtree(size_t __v) const {
    assert(made());
    return {__in[__v], __out[__v]};
  }

  /**
   * @param __v
   * @return Return v if v is the root.
   */
  size_t parent(size_t __v) const {
    assert(made());
    return __head[__v] < size() ? prev_node(__v) : __head[__v] - size();
  }

  size_t top(size_t __v) const {
    assert(made());
    return __head[__v] < size() ? __head[__v] : __v;
  }

  /**
   * @brief Get LCA in O(log(size)) time.
   * @param __u 1st node
   * @param __v 2nd node
   * @return Lowest Common Ancestor of the two.
   */
  size_t lca(size_t __u, size_t __v) const {
    assert(made());
    if (__in[__v] < __in[__u]) std::swap(__u, __v);
    if (__in[__v] < __out[__u]) return __u;
    while (__in[__u] < __in[__v]) __v = parent(top(__v));
    return __v;
  }

  /**
   * @brief Ancestor.
   * @return k-th ancestor of v.
   */
  size_t ancestor(size_t __v, size_t __k) const {
    assert(made());
    while (__k) {
      assert(__in[__v]);
      auto __t = top(__v);
      auto __d = __in[__v] - __in[__t];
      if (__d < __k) {
        __k -= __d + 1;
        __v = __head[__t] - size();
      } else {
        __v = __sorted[__in[__v] - __k];
        __k = 0;
      }
    }
    return __v;
  }

  size_t depth(size_t __v) const { return __depth[__v]; }

  size_t distance(size_t __u, size_t __v) const {
    return __depth[__u] + __depth[__v] - __depth[lca(__u, __v)] * 2;
  }

  /**
   * @brief Split a path into O(log(size)) paths.
   * @return Pair of list of ascending paths. first.back() is the index of
   * lca(u, v).
   */
  auto split_path(size_t __u, size_t __v) const {
    assert(made());
    if (__in[__v] < __in[__u]) std::swap(__u, __v);
    std::vector<std::pair<size_t, size_t>> left, right;
    auto utop = top(__u), vtop = top(__v);
    while (utop != vtop) {
      left.emplace_back(__in[vtop], __in[__v] + 1);
      vtop = top(__v = parent(vtop));
      if (__in[__v] < __in[__u]) {
        std::swap(__u, __v);
        std::swap(utop, vtop);
        std::swap(left, right);
      }
    }
    left.emplace_back(__in[__u], __in[__v] + 1);
    return std::make_pair(left, right);
  }

  /**
   * @brief Split a path upto root() into O(log(size)) paths.
   * @return List of ascending paths. back() is the index of lca(root(), v).
   */
  auto split_path(size_t __v) const {
    assert(made());
    auto [left, right] = split_path(root(), __v);
    right.insert(right.begin(), left.begin(), left.end());
    return right;
  }
};
#line 2 "src/utils/io/istream.hpp"

/**
 * @file istream.hpp
 * @brief Input Stream
 */

#include <cxxabi.h>

#line 12 "src/utils/io/istream.hpp"
#include <tuple>

#line 16 "src/utils/io/istream.hpp"

namespace workspace {

namespace _istream_impl {

template <class _Tp, typename = void> struct helper {
  helper(std::istream &__is, _Tp &__x) {
    if _CXX17_CONSTEXPR (has_begin<_Tp &>::value)
      for (auto &&__e : __x) helper<std::decay_t<decltype(__e)>>(__is, __e);
    else
      static_assert(has_begin<_Tp>::value, "istream unsupported type.");
  }
};

template <class _Tp>
struct helper<_Tp, std::__void_t<decltype(std::declval<std::istream &>() >>
                                          std::declval<_Tp &>())>> {
  helper(std::istream &__is, _Tp &__x) { __is >> __x; }
};

#ifdef __SIZEOF_INT128__

template <> struct helper<__uint128_t, void> {
  helper(std::istream &__is, __uint128_t &__x) {
    std::string __s;
    __is >> __s;
    bool __neg = false;
    if (__s.front() == '-') __neg = true, __s.erase(__s.begin());
    __x = 0;
    for (char __d : __s) {
      __x *= 10;
      __d -= '0';
      if (__neg)
        __x -= __d;
      else
        __x += __d;
    }
  }
};

template <> struct helper<__int128_t, void> {
  helper(std::istream &__is, __int128_t &__x) {
    std::string __s;
    __is >> __s;
    bool __neg = false;
    if (__s.front() == '-') __neg = true, __s.erase(__s.begin());
    __x = 0;
    for (char __d : __s) {
      __x *= 10;
      __d -= '0';
      if (__neg)
        __x -= __d;
      else
        __x += __d;
    }
  }
};

#endif  // INT128

template <class _T1, class _T2> struct helper<std::pair<_T1, _T2>> {
  helper(std::istream &__is, std::pair<_T1, _T2> &__x) {
    helper<_T1>(__is, __x.first), helper<_T2>(__is, __x.second);
  }
};

template <class... _Tp> struct helper<std::tuple<_Tp...>> {
  helper(std::istream &__is, std::tuple<_Tp...> &__x) { iterate(__is, __x); }

 private:
  template <class _Tuple, size_t _Nm = 0>
  void iterate(std::istream &__is, _Tuple &__x) {
    if _CXX17_CONSTEXPR (_Nm != std::tuple_size<_Tuple>::value) {
      helper<typename std::tuple_element<_Nm, _Tuple>::type>(
          __is, std::get<_Nm>(__x)),
          iterate<_Tuple, _Nm + 1>(__is, __x);
    }
  }
};

}  // namespace _istream_impl

/**
 * @brief A wrapper class for std::istream.
 */
class istream : public std::istream {
 public:
  /**
   * @brief Wrapped operator.
   */
  template <typename _Tp> istream &operator>>(_Tp &__x) {
    _istream_impl::helper<_Tp>(*this, __x);
    if (std::istream::fail()) {
      static auto once = atexit([] {
        std::cerr << "\n\033[43m\033[30mwarning: failed to read \'"
                  << abi::__cxa_demangle(typeid(_Tp).name(), 0, 0, 0)
                  << "\'.\033[0m\n\n";
      });
      assert(!once);
    }
    return *this;
  }
};

decltype(auto) cin = static_cast<istream &>(std::cin);

}  // namespace workspace
#line 9 "test/aizu-online-judge/2450.test.cpp"

int main() {
  struct endo {
    bool assign = false;
    int value;
    endo() = default;
    endo(int v) : assign(true), value(v) {}
    endo operator*(endo rhs) const {
      if (rhs.assign) return rhs;
      return *this;
    }
  };
  struct mono {
    int cnt = 0;
    int sum = 0;
    int left = 0;
    int right = 0;
    int max = 0;
    int best = -10000;
    mono operator+(mono rhs) const {
      return {cnt + rhs.cnt,
              sum + rhs.sum,
              std::max(left, sum + rhs.left),
              std::max(right + rhs.sum, rhs.right),
              std::max({max, rhs.max, right + rhs.left}),
              std::max(best, rhs.best)};
    }
    mono operator*(endo rhs) const {
      mono ret = *this;
      if (rhs.assign) {
        if (rhs.value < 0) {
          ret.sum = rhs.value * cnt;
          ret.left = ret.right = ret.max = 0;
        } else {
          ret.sum = ret.left = ret.right = ret.max = cnt * rhs.value;
        }
        ret.best = rhs.value;
      }
      return ret;
    }
  };

  int n, q;
  std::cin >> n >> q;
  std::vector<int> w(n);
  workspace::cin >> w;
  heavy_light_decomposition hld(n);
  for (auto e = 1; e != n; ++e) {
    int u, v;
    std::cin >> u >> v;
    --u, --v;
    hld.add_edge(u, v);
  }
  hld.make(0);
  workspace::lazy_segment_tree<mono, endo> seg(n);
  for (auto v = 0; v != n; ++v) {
    auto &now = seg[hld.index(v)];
    now.cnt = 1;
    now = now * endo{w[v]};
  }
  while (q--) {
    int tp, a, b, c;
    std::cin >> tp >> a >> b >> c;
    --a, --b;
    auto [left, right] = hld.split_path(a, b);
    if (tp == 1) {
      for (auto &&[l, r] : left) {
        seg.apply(l, r, c);
      }
      for (auto &&[l, r] : right) {
        seg.apply(l, r, c);
      }
    } else {
      mono lv;
      for (auto &&[l, r] : left) {
        lv = seg.fold(l, r) + lv;
      }
      mono rv;
      for (auto &&[l, r] : right) {
        rv = seg.fold(l, r) + rv;
      }
      std::swap(lv.left, lv.right);
      auto all = lv + rv;
      if (all.max)
        std::cout << all.max << '\n';
      else
        std::cout << all.best << '\n';
    }
  }
}
Back to top page