Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 26 additions & 20 deletions other_algorithms/doubling.hpp
Original file line number Diff line number Diff line change
@@ -1,45 +1,51 @@
#pragma once
#include <cstdlib>
#include <cassert>
#include <vector>

// CUT begin
// Binary lifting / `Doubling`
// Complexity: O(NlogN) precalculation / O(logN) per query
// <https://atcoder.jp/contests/arc060/submissions/7039451>
// 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<std::vector<int>> mat;
BinaryLifting() : N(0), lgD(0) {}
BinaryLifting(const std::vector<int> &vec_nxt, int INVALID = -1, int lgd = 0)
: N(vec_nxt.size()), INVALID(INVALID), lgD(lgd) {
BinaryLifting(const std::vector<int> &to, int lgd = 0) : N(to.size()), lgD(lgd) {
while ((1LL << lgD) < N) lgD++;
mat.assign(lgD, std::vector<int>(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<int>(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
}
}
};
28 changes: 28 additions & 0 deletions other_algorithms/doubling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
title: Binary lifting / doubling (ダブリング)
documentation_of: ./doubling.hpp
---

Functional graph 上のダブリングライブラリ. binary_lifting.hpp とは異なり辺に重みなどは載せられない.

## 使用方法

### `BinaryLifting binary_lifting(std::vector<int> 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)
35 changes: 35 additions & 0 deletions other_algorithms/test/doubling.yuki3305.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#define PROBLEM "https://yukicoder.me/problems/no/3305"
#include "../doubling.hpp"

#include <iostream>
#include <utility>
#include <vector>
using namespace std;

int main() {
cin.tie(nullptr), ios::sync_with_stdio(false);

int N, Q;
cin >> N >> Q;
vector<int> A(N);
for (auto &a : A) cin >> a;

vector<int> nexts(N, N);

vector<pair<int, int>> 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';
}
}
2 changes: 1 addition & 1 deletion segmenttree/acl_lazysegtree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ constexpr int countr_zero_constexpr(unsigned int n) {
#include <functional>
#include <vector>

#include "atcoder/internal_bit"
// #include "atcoder/internal_bit"

namespace atcoder {

Expand Down