Skip to content

range_product_changes.hpp

SECTIONMath INCLUDEnoya/math/range_product_changes.hpp

按左端点枚举区间积的连续变化段,记录每段首次出现的右端点;支持结合运算,常用于 gcd、按位与、按位或。

\[ \displaystyle p_{l,r}=a_l\circ a_{l+1}\circ\cdots\circ a_{r-1} \]

Complexity: Time: \(O(n + S)\) operation/equality calls, where \(S\) is the number of returned runs. Space: \(O(1)\) auxiliary values, excluding \(O(n + S)\) output.

跳到代码 · GitHub ↗

Implementation

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

/// @complexity Time: O(n + S) operation/equality calls, where S is the number
/// of returned runs. Space: O(1) auxiliary values, excluding O(n + S) output.

#include <utility>
#include <vector>

namespace noya {

/// @brief Compress consecutive equal products for each left endpoint.
/// The operation must be associative. Nonconsecutive repeated values are kept.
/// @return For every left, pairs (right, product(A[left:right])) at the first
/// right where the product changes, ordered by increasing right.
template <class T, auto op>
std::vector<std::vector<std::pair<int, T>>>
range_product_changes(const std::vector<T> &A) {
  const int N = int(A.size());
  std::vector<std::vector<std::pair<int, T>>> res(N);
  for (int l = N - 1; l >= 0; --l) {
    auto &cur = res[l];
    cur.reserve(l + 1 < N ? res[l + 1].size() + 1 : 1);
    cur.emplace_back(l + 1, A[l]);
    if (l + 1 == N) continue;
    for (const auto &[r, prd] : res[l + 1]) {
      T ext = op(A[l], prd);
      if (!(cur.back().second == ext)) cur.emplace_back(r, std::move(ext));
    }
  }
  return res;
}

} // namespace noya

 // NOYA_RANGE_PRODUCT_CHANGES_HPP
#ifndef NOYA_RANGE_PRODUCT_CHANGES_HPP
#define NOYA_RANGE_PRODUCT_CHANGES_HPP 1

/// @complexity Time: O(n + S) operation/equality calls, where S is the number
/// of returned runs. Space: O(1) auxiliary values, excluding O(n + S) output.

#include <utility>
#include <vector>

namespace noya {

/// @brief Compress consecutive equal products for each left endpoint.
/// The operation must be associative. Nonconsecutive repeated values are kept.
/// @return For every left, pairs (right, product(A[left:right])) at the first
/// right where the product changes, ordered by increasing right.
template <class T, auto op>
std::vector<std::vector<std::pair<int, T>>>
range_product_changes(const std::vector<T> &A) {
  const int N = int(A.size());
  std::vector<std::vector<std::pair<int, T>>> res(N);
  for (int l = N - 1; l >= 0; --l) {
    auto &cur = res[l];
    cur.reserve(l + 1 < N ? res[l + 1].size() + 1 : 1);
    cur.emplace_back(l + 1, A[l]);
    if (l + 1 == N) continue;
    for (const auto &[r, prd] : res[l + 1]) {
      T ext = op(A[l], prd);
      if (!(cur.back().second == ext)) cur.emplace_back(r, std::move(ext));
    }
  }
  return res;
}

} // namespace noya

#endif // NOYA_RANGE_PRODUCT_CHANGES_HPP
/// @complexity Time: O(n + S) operation/equality calls, where S is the number
/// of returned runs. Space: O(1) auxiliary values, excluding O(n + S) output.

#include <utility>
#include <vector>

namespace noya {

/// @brief Compress consecutive equal products for each left endpoint.
/// The operation must be associative. Nonconsecutive repeated values are kept.
/// @return For every left, pairs (right, product(A[left:right])) at the first
/// right where the product changes, ordered by increasing right.
template <class T, auto op>
std::vector<std::vector<std::pair<int, T>>>
range_product_changes(const std::vector<T> &A) {
  const int N = int(A.size());
  std::vector<std::vector<std::pair<int, T>>> res(N);
  for (int l = N - 1; l >= 0; --l) {
    auto &cur = res[l];
    cur.reserve(l + 1 < N ? res[l + 1].size() + 1 : 1);
    cur.emplace_back(l + 1, A[l]);
    if (l + 1 == N) continue;
    for (const auto &[r, prd] : res[l + 1]) {
      T ext = op(A[l], prd);
      if (!(cur.back().second == ext)) cur.emplace_back(r, std::move(ext));
    }
  }
  return res;
}

} // namespace noya

 // NOYA_RANGE_PRODUCT_CHANGES_HPP