From 6062bbb4807a424fc4ca238edd32b81d18804af3 Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:40:50 +0900 Subject: [PATCH 1/2] fix doubling --- other_algorithms/doubling.hpp | 46 +++++++++++-------- other_algorithms/doubling.md | 28 +++++++++++ .../test/doubling.yuki3305.test.cpp | 35 ++++++++++++++ 3 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 other_algorithms/doubling.md create mode 100644 other_algorithms/test/doubling.yuki3305.test.cpp diff --git a/other_algorithms/doubling.hpp b/other_algorithms/doubling.hpp index 039752bc..bf0c9c2d 100644 --- a/other_algorithms/doubling.hpp +++ b/other_algorithms/doubling.hpp @@ -1,45 +1,51 @@ #pragma once -#include +#include #include -// CUT begin // Binary lifting / `Doubling` // Complexity: O(NlogN) precalculation / O(logN) per query -// +// https://atcoder.jp/contests/arc060/submissions/7039451 struct BinaryLifting { - int N, INVALID, lgD; + int N, lgD; + + bool is_valid(int idx) const { return 0 <= idx and idx < N; } + std::vector> mat; BinaryLifting() : N(0), lgD(0) {} - BinaryLifting(const std::vector &vec_nxt, int INVALID = -1, int lgd = 0) - : N(vec_nxt.size()), INVALID(INVALID), lgD(lgd) { + BinaryLifting(const std::vector &to, int lgd = 0) : N(to.size()), lgD(lgd) { while ((1LL << lgD) < N) lgD++; - mat.assign(lgD, std::vector(N, INVALID)); - mat[0] = vec_nxt; - for (int i = 0; i < N; i++) - if (mat[0][i] < 0 or mat[0][i] >= N) mat[0][i] = INVALID; + mat.assign(lgD, std::vector(N)); + mat[0] = to; + for (int d = 0; d < lgD - 1; d++) { - for (int i = 0; i < N; i++) - if (mat[d][i] != INVALID) mat[d + 1][i] = mat[d][mat[d][i]]; + for (int i = 0; i < N; i++) { + mat[d + 1][i] = mat[d][is_valid(mat[d][i]) ? mat[d][i] : i]; + } } } - int kth_next(int now, long long k) { - if (k >= (1LL << lgD)) exit(8); - for (int d = 0; k and now != INVALID; d++, k >>= 1) + + int kth_next(int now, long long k) const { + assert(k >= 0); + assert(k < (1LL << lgD)); + for (int d = 0; k and is_valid(now); d++, k >>= 1) { if (k & 1) now = mat[d][now]; + } return now; } // Distance from l to [r, \infty) - // Requirement: mat[0][i] > i for all i (monotone increasing) - int distance(int l, int r) { + // Requirement: mat[0][i] >= r (i = r, r + 1, ...) (monotone) + int distance(int l, int r) const { if (l >= r) return 0; int ret = 0; for (int d = lgD - 1; d >= 0; d--) { - if (mat[d][l] < r and mat[d][l] != INVALID) ret += 1 << d, l = mat[d][l]; + if (mat[d][l] < r and is_valid(mat[d][l])) ret += 1 << d, l = mat[d][l]; } - if (mat[0][l] == INVALID or mat[0][l] >= r) + + if (!is_valid(mat[0][l]) or mat[0][l] >= r) { return ret + 1; - else + } else { return -1; // Unable to reach + } } }; diff --git a/other_algorithms/doubling.md b/other_algorithms/doubling.md new file mode 100644 index 00000000..b0550da3 --- /dev/null +++ b/other_algorithms/doubling.md @@ -0,0 +1,28 @@ +--- +title: Binary lifting / doubling (ダブリング) +documentation_of: ./doubling.hpp +--- + +Functional graph 上のダブリングライブラリ. binary_lifting.hpp とは異なり辺に重みなどは載せられない. + +## 使用方法 + +### `BinaryLifting binary_lifting(std::vector to)` + +コンストラクタ.引数として $g(0), \ldots, g(n - 1)$ を与える. + +直感的には,各頂点 $i = 0, \ldots, n - 1$ について $i$ から頂点 $g(i)$ へ有向辺が張られている functional graph に相当する. $g(i)$ の値は $0$ 未満や $n$ 以上でも構わない(下記の各関数は, $[0, n)$ の範囲外の頂点 $i$ からは $i$ 自身への重み $e$ の自己ループが生えている,と解釈するとつじつまが合うように設計されている). + +### `int kth_next(int s, long long k)` + +$g^k (s)$ の値(途中で $[0, n)$ の範囲外に出る場合はその値)を $O(\log k)$ で返す. + +### `int distance(int l, int r)` + +$g^k (s)$ の値が初めて `r` 以上になる $k$ を計算する.この条件が満たされることはない場合は `-1` を返す. + +この条件に関する単調性が必要. + +## 問題例 + +- [No.3305 Shift Sort - yukicoder](https://yukicoder.me/problems/no/3305) diff --git a/other_algorithms/test/doubling.yuki3305.test.cpp b/other_algorithms/test/doubling.yuki3305.test.cpp new file mode 100644 index 00000000..d72385d9 --- /dev/null +++ b/other_algorithms/test/doubling.yuki3305.test.cpp @@ -0,0 +1,35 @@ +#define PROBLEM "https://yukicoder.me/problems/no/3305" +#include "../doubling.hpp" + +#include +#include +#include +using namespace std; + +int main() { + cin.tie(nullptr), ios::sync_with_stdio(false); + + int N, Q; + cin >> N >> Q; + vector A(N); + for (auto &a : A) cin >> a; + + vector nexts(N, N); + + vector> st; + st.emplace_back(N, N); + for (int i = N - 1; i >= 0; --i) { + while (st.back().first < A.at(i)) st.pop_back(); + nexts.at(i) = st.back().second; + st.emplace_back(A.at(i), i); + } + + const BinaryLifting binary_lifting(nexts); + + while (Q--) { + int l, r; + cin >> l >> r; + --l; + cout << r - l - binary_lifting.distance(l, r) << '\n'; + } +} From 1a32aa19470b7cee5335b008300aeb24bedfe663 Mon Sep 17 00:00:00 2001 From: hitonanode <32937551+hitonanode@users.noreply.github.com> Date: Tue, 7 Oct 2025 09:41:07 +0900 Subject: [PATCH 2/2] fix lazy segtree --- segmenttree/acl_lazysegtree.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/segmenttree/acl_lazysegtree.hpp b/segmenttree/acl_lazysegtree.hpp index e5e76430..7b8b2e03 100644 --- a/segmenttree/acl_lazysegtree.hpp +++ b/segmenttree/acl_lazysegtree.hpp @@ -60,7 +60,7 @@ constexpr int countr_zero_constexpr(unsigned int n) { #include #include -#include "atcoder/internal_bit" +// #include "atcoder/internal_bit" namespace atcoder {