※この解説はhamayanhamayanさんの解説と同じ内容です。自分の理解のためにまとめ直しました。
執筆時レート:水色
問題文→https://atcoder.jp/contests/abc164/tasks/abc164_d
公式解説→https://atcoder.jp/contests/abc164/tasks/abc164_d/editorial
目次
問題
から までの数字のみからなる文字列 が与えられる。
次の条件を満たす整数の組 の総数を求めよ。
条件: の 文字目から 文字目までを 進法の整数としてみると,この整数は の倍数である。
制約
- は から までの数字のみからなる文字列
解説
区間数え上げは片方を固定し、もう片方を高速に数え上げるのが定石。
例えば数え上げ方の一つに尺取法がある。尺取法が有効なのは,片方を固定したとき,もう片方について「ある条件を満たす/満たさない」の単調性が見られる場合である。満たす/満たさないの境界が分れば,満たす側の個数を計上すれば良い。
しかし,今回の問題で尺取法は使えない。というのも,一般に単調性がないためだ。
入力例1「」を見てみる。
区間の左端を一番左に固定し右端を右に伸ばしていくと,「」までは の倍数でなく,次の「」で初めて の倍数になるが,その次の「」は の倍数でない。このように,右端を動かしたときに条件「 の倍数である」を満たす/満たさないの境界がただ 1 つでない。だから尺取法は有効でない。
別の方法を考えてみる。
例えば,区間 は半開区間の引き算 と表現することができる。累積和がその代表的な例の 1 つだ。
これを利用できないか考えてみる。
先ほどの例示では左端 0 を支点としていたが,今回の問題は(それでは上手くいかないので)右端を支点にしてみる。
説明のため,文字や表記を次のように定義する。 は 0-indexed である。
- の 文字目から 文字目までを 進法の整数としてみたときの整数。ただし, (末尾)の場合は と略記する。
問題文の条件を変形していく。
(と は互いに素なので も成り立つ)
つまり, 文字目以降の部分を で割った余りと 文字目以降の部分を で割った余りが等しければ, 文字目から 文字目までの部分は の倍数になる,ということである。
よって, 文字目以降の部分の余りを集計しつつ,右から を走査をすれば良い。
を計算し,それと同じ値のものがこれまでにあれば,その個数が として取り得るものの個数である。
は前の を使い,
という式で で求められる。
を全探索し,各 での処理は なので,全体の計算量は である。
実装
C++による実装例。コメント付き。
https://atcoder.jp/contests/abc164/submissions/39539547