Skip to content

Commit 7c2d256

Browse files
authored
Merge pull request #405 from hitonanode/fix-doubling
Fix doubling
2 parents a665350 + 1a32aa1 commit 7c2d256

File tree

4 files changed

+90
-21
lines changed

4 files changed

+90
-21
lines changed

other_algorithms/doubling.hpp

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
11
#pragma once
2-
#include <cstdlib>
2+
#include <cassert>
33
#include <vector>
44

5-
// CUT begin
65
// Binary lifting / `Doubling`
76
// Complexity: O(NlogN) precalculation / O(logN) per query
8-
// <https://atcoder.jp/contests/arc060/submissions/7039451>
7+
// https://atcoder.jp/contests/arc060/submissions/7039451
98
struct BinaryLifting {
10-
int N, INVALID, lgD;
9+
int N, lgD;
10+
11+
bool is_valid(int idx) const { return 0 <= idx and idx < N; }
12+
1113
std::vector<std::vector<int>> mat;
1214
BinaryLifting() : N(0), lgD(0) {}
13-
BinaryLifting(const std::vector<int> &vec_nxt, int INVALID = -1, int lgd = 0)
14-
: N(vec_nxt.size()), INVALID(INVALID), lgD(lgd) {
15+
BinaryLifting(const std::vector<int> &to, int lgd = 0) : N(to.size()), lgD(lgd) {
1516
while ((1LL << lgD) < N) lgD++;
16-
mat.assign(lgD, std::vector<int>(N, INVALID));
17-
mat[0] = vec_nxt;
18-
for (int i = 0; i < N; i++)
19-
if (mat[0][i] < 0 or mat[0][i] >= N) mat[0][i] = INVALID;
17+
mat.assign(lgD, std::vector<int>(N));
18+
mat[0] = to;
19+
2020
for (int d = 0; d < lgD - 1; d++) {
21-
for (int i = 0; i < N; i++)
22-
if (mat[d][i] != INVALID) mat[d + 1][i] = mat[d][mat[d][i]];
21+
for (int i = 0; i < N; i++) {
22+
mat[d + 1][i] = mat[d][is_valid(mat[d][i]) ? mat[d][i] : i];
23+
}
2324
}
2425
}
25-
int kth_next(int now, long long k) {
26-
if (k >= (1LL << lgD)) exit(8);
27-
for (int d = 0; k and now != INVALID; d++, k >>= 1)
26+
27+
int kth_next(int now, long long k) const {
28+
assert(k >= 0);
29+
assert(k < (1LL << lgD));
30+
for (int d = 0; k and is_valid(now); d++, k >>= 1) {
2831
if (k & 1) now = mat[d][now];
32+
}
2933
return now;
3034
}
3135

3236
// Distance from l to [r, \infty)
33-
// Requirement: mat[0][i] > i for all i (monotone increasing)
34-
int distance(int l, int r) {
37+
// Requirement: mat[0][i] >= r (i = r, r + 1, ...) (monotone)
38+
int distance(int l, int r) const {
3539
if (l >= r) return 0;
3640
int ret = 0;
3741
for (int d = lgD - 1; d >= 0; d--) {
38-
if (mat[d][l] < r and mat[d][l] != INVALID) ret += 1 << d, l = mat[d][l];
42+
if (mat[d][l] < r and is_valid(mat[d][l])) ret += 1 << d, l = mat[d][l];
3943
}
40-
if (mat[0][l] == INVALID or mat[0][l] >= r)
44+
45+
if (!is_valid(mat[0][l]) or mat[0][l] >= r) {
4146
return ret + 1;
42-
else
47+
} else {
4348
return -1; // Unable to reach
49+
}
4450
}
4551
};

other_algorithms/doubling.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
title: Binary lifting / doubling (ダブリング)
3+
documentation_of: ./doubling.hpp
4+
---
5+
6+
Functional graph 上のダブリングライブラリ. binary_lifting.hpp とは異なり辺に重みなどは載せられない.
7+
8+
## 使用方法
9+
10+
### `BinaryLifting binary_lifting(std::vector<int> to)`
11+
12+
コンストラクタ.引数として $g(0), \ldots, g(n - 1)$ を与える.
13+
14+
直感的には,各頂点 $i = 0, \ldots, n - 1$ について $i$ から頂点 $g(i)$ へ有向辺が張られている functional graph に相当する. $g(i)$ の値は $0$ 未満や $n$ 以上でも構わない(下記の各関数は, $[0, n)$ の範囲外の頂点 $i$ からは $i$ 自身への重み $e$ の自己ループが生えている,と解釈するとつじつまが合うように設計されている).
15+
16+
### `int kth_next(int s, long long k)`
17+
18+
$g^k (s)$ の値(途中で $[0, n)$ の範囲外に出る場合はその値)を $O(\log k)$ で返す.
19+
20+
### `int distance(int l, int r)`
21+
22+
$g^k (s)$ の値が初めて `r` 以上になる $k$ を計算する.この条件が満たされることはない場合は `-1` を返す.
23+
24+
この条件に関する単調性が必要.
25+
26+
## 問題例
27+
28+
- [No.3305 Shift Sort - yukicoder](https://yukicoder.me/problems/no/3305)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#define PROBLEM "https://yukicoder.me/problems/no/3305"
2+
#include "../doubling.hpp"
3+
4+
#include <iostream>
5+
#include <utility>
6+
#include <vector>
7+
using namespace std;
8+
9+
int main() {
10+
cin.tie(nullptr), ios::sync_with_stdio(false);
11+
12+
int N, Q;
13+
cin >> N >> Q;
14+
vector<int> A(N);
15+
for (auto &a : A) cin >> a;
16+
17+
vector<int> nexts(N, N);
18+
19+
vector<pair<int, int>> st;
20+
st.emplace_back(N, N);
21+
for (int i = N - 1; i >= 0; --i) {
22+
while (st.back().first < A.at(i)) st.pop_back();
23+
nexts.at(i) = st.back().second;
24+
st.emplace_back(A.at(i), i);
25+
}
26+
27+
const BinaryLifting binary_lifting(nexts);
28+
29+
while (Q--) {
30+
int l, r;
31+
cin >> l >> r;
32+
--l;
33+
cout << r - l - binary_lifting.distance(l, r) << '\n';
34+
}
35+
}

segmenttree/acl_lazysegtree.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ constexpr int countr_zero_constexpr(unsigned int n) {
6060
#include <functional>
6161
#include <vector>
6262

63-
#include "atcoder/internal_bit"
63+
// #include "atcoder/internal_bit"
6464

6565
namespace atcoder {
6666

0 commit comments

Comments
 (0)