AtCoder Beginner Contest 129 F - Takahashi's Basics in Education and Learning

F - Takahashi’s Basics in Education and Learning

問題

初項 $a$ ,公比 $b$ で長さ $l$ の等差数列を,第 1 項から連結してできる整数を $m$ で割った余りを求めよ.

制約

  • $1 \leq l, a, b \lt 10^{18}$
  • $2 \leq m \leq 10^9$
  • 等差数列の項は全て $10^{18}$ 未満

考察

まず,桁数によって数列を分割する. $k$ 桁の要素は $[10^{k-1}, 10^k)$ に属するので, $p(x) =$ 「 $x$ 以上で最小の項」とすれば,第 $\left[ p(10^{k - 1}), p(10^k) \right)$ 項が $k$ 桁の区間となる. そして $p(x) = \lceil \frac{x - a}{b} \rceil$ で計算できるので,これで桁毎に分割できた.

第 $[l, r)$ 項が $k$ 桁の区間とする.ここで

$$ \begin{aligned} s_0 &= a \\ c_0 &= 0 \\ s_{n + 1} &= s_n + b \\ c_{n + 1} &= 10^k c_n + s_n \end{aligned} $$

とすれば, $c_{r-l}$ が $k$ 桁の項を全て連結した値となる.そしてこの数列は,

$$ \begin{pmatrix} c_{m + 1} \\ s_{m + 1} \\ 1 \end{pmatrix} = \begin{pmatrix} 10^k & 1 & 0 \\ 0 & 1 & b \\ 0 & 0 & 1 \\ \end{pmatrix} \begin{pmatrix} c_m \\ s_m \\ 1 \end{pmatrix} $$

と表現できるので,行列累乗によって $\Theta(\log(r - l))$ で $c_{r - l}$ が求められる.

実装例

提出 #8223429 - AtCoder Beginner Contest 129

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <vector>

template <class T, class Int>
T ipow(T b, Int n) { ... }

template <class T>
using Vector = std::vector<T>;
template <class T>
using Matrix = Vector<Vector<T>>;

template <class T>
Matrix<T> operator*(const Matrix<T>& a, const Matrix<T>& b) { ... }
template <class T, class Int>
Matrix<T> mpow(Matrix<T> b, Int n) { ... }

struct ModInt { ... };

int ModInt::MOD;
using mint = ModInt;

using lint = long long;

int main() {
    lint l, a, b;
    std::cin >> l >> a >> b >> ModInt::MOD;

    auto ith = [&](lint x) {
        return (x <= a - b ? -1 : (x - a + b - 1) / b);
    };
    // x以上の最小の項

    mint ans = 0;
    for (int k = 1; k <= 18; ++k) {
        lint low = std::max(0LL, ith(ipow(10LL, k - 1))),
             high = std::min(l, ith(ipow(10LL, k)));
        // [low, high)項がk桁
        if (high < 0) continue;
        if (low > l) break;

        lint len = high - low;
        ans *= ipow(ipow(mint(10), k), len);
        // !! k*lenはオーバーフローするので注意 !!

        Matrix<mint> mat{{ipow(mint(10), k), 1, 0},
                         {0, 1, b},
                         {0, 0, 1}};
        mat = mpow(mat, len);
        ans += mat[0][1] * (a + b * low) + mat[0][2];
    }

    std::cout << ans << std::endl;
    return 0;
}
Built with Hugo
テーマ StackJimmy によって設計されています。