ダブリングを行うライブラリを書きました.ソースはこちら .
1. できること
1.1 基本用法
$f : [0, N) \to [0, N)$ が与えられた時, $r \in [0, R]$ と $i \in [0, N)$ に対して $f^{r}(i)$ を 計算する.
1.2 追加用法
上の $f$ とともに,モノイド $(M, \oplus)$ と $m: [0, N) \to M$ も与えられて, $r, i$ に対して $\bigoplus_{k = 0}^{r - 1} m(f^{k}(i))$ も計算する.
典型例:
- $i \mapsto f(i)$ を1回実行するごとに $m(i)$ だけのコストがかかるとき, $f^r(i)$ の実行に要するコストを求める.
- $[0..N-1]$ が円環状に並んでいる.$f^r(i)$ の実行で何周するか求める.
- この場合には,$m(i) := \textrm{ if } r(i) < i \textrm{ then } 1 \textrm{ else } 0$ とすればよい.
- tree の先祖-子孫パス上の辺の重みの最大値を求める
1.3 二分探索
$P$ を $M$上の単調な述語として,$i$ に対して $P(\bigoplus_{k = 0}^{r - 1} m(f^{k}(i)))$ が成り立つ最大/最小の $r$ を求める.
1.4 計算量
- 前計算 $O(N \log R)$
- 各回の値の計算 $O(\log R)$
- 二分探索 $O(\log R)$
2. 使用法
Doubling オブジェクト dobj を作って使う.
2.1 基本用法だけの時
オブジェクトの作成
関数 make_doubling を用いて,オブジェクトを作成する.
vector<int> vecF(N); ... // vecF[i] が関数 f(i) の値になるようにする. vector<ll> でも可.
auto dobj = make_doubling(R, vecF);
R は,$f^r$ を計算する $r$ の最大値以上に取る.等しくとも良い.
オブジェクトを作成すると,内部テーブルの dobj.f_tbl[k][i] に,$f^{2^k}(i)$ の値が格納される.
値の取得
メンバ関数 f_val を用いて,値が取得できる.
int x = dobj.f_val(r, i); // f^r(i) の値
2.2 追加用法も行う時
オブジェクトの作成
関数 make_doubling に,マッピング $m$,モノイド単位元,モノイド演算も与える.
vector<int> vecF(N); ... // 同上
vector<T> vecM(N); ... // vecM[i] がマッピング m(i) の値になるようにする.
T unit = ...; // モノイド単位元
auto prod = [](const T& a, const T& b) -> T { ... }; // モノイド演算
auto dobj = make_doubling(R, vecF, vecM, unit, prod);
よく使うケースとしては,make_doubling(R, vecF, vecM, 0LL, plus<ll>()) など.
内部テーブルの dobj.t_tbl[k][i] に,$\bigoplus_{j = 0}^{2^k - 1} m(f^{j}(i))$ の値が格納される.
値の取得
メンバ関数 val_pair で,$f^r(i)$ と $\bigoplus_{k = 0}^{r - 1} m(f^{k}(i))$ の値のペアが取得できる. 片方のみで良いときには,f_val, t_val も使える.
auto [idx, val] = dobj.val_pair(r, i);
int idx2 = dobj.f_val(r, i); // idx == idx2
T val2 = dobj.t_val(r, i); // val == val2
2.3. 二分探索
メンバ関数 binsearh_lo と binsearch_hi がある. $P$ を $M$ 上の述語とする. $P’(r, i) := P(\bigoplus_{k = 0}^{r - 1} m(f^{k}(i)))$ として,これが $r$ について単調であるとする.
- binsearch_lo: 述語 $P’$ が,r が小さい時に成り立つ場合,成り立つ最大の r を求める.
- binsearch_hi: 述語 $P’$ が,r が大きい時に成り立つ場合,成り立つ最大の r を求める.
auto check0 = [&](T t) -> bool { /* P0(t) が成り立つとき true を返す */ };
...
ll r0 = binsearch_lo(check0, i); // P0'(r, i) が成り立つ最大の r
ll r1 = binsearch_hi(check1, i); // P1'(r, i) が成り立つ最小の r
述語を成り立たせる $r$ が存在しないときには,binsearch_lo は -1 を,binsearch_hi は R + 1 を返す.
どちらのメンバ関数も,省略可能な引数 lo (既定値 0) と hi (既定値は -1 だが,これは R を意味する) を持っていて, 指定するとその範囲で探索が行われる.単調性もその範囲で保証すれば良い.
keywords: doubling