B - 連番チケットの当選 / Winning of Consecutive Number Tickets Editorial by admin
gemini-3.5-flash-high概要
この問題は、連続する3つの整数 \(x-1, x, x+1\) の桁和を \(K\) で割った余りがすべて \(R\) となるような、範囲 \(2 \leq x \leq N - 1\) における \(x\) の個数を求める問題です。
一見すると複雑な桁DP(動的計画法)などが必要そうに見えますが、隣り合う整数の桁和の関係性に着目することで、極めてシンプルな規則性を見つけることができます。
考察
1. 隣り合う整数の桁和の関係
整数 \(y\) から \(y+1\) に変化するときの桁和の変化を考えてみましょう。
繰り上がりが発生しない場合(\(y\) の一の位が \(9\) 以外の場合) 一の位が \(1\) 増えるだけなので、桁和は必ず \(1\) 増えます。 $\(\mathrm{digitsum}(y+1) = \mathrm{digitsum}(y) + 1\)$
繰り上がりが発生する場合(\(y\) の一の位が \(9\) の場合) 下位に \(9\) が連続して \(d\) 個並んでいるとき、それらがすべて \(0\) になり、その上の桁が \(1\) 増えます。このとき、桁和は \(9d - 1\) 減少します。 $\(\mathrm{digitsum}(y+1) = \mathrm{digitsum}(y) + 1 - 9d\)$
2. 連続する3つの整数における繰り上がり
連続する3つの整数 \(x-1, x, x+1\) について考えます。 一の位が \(9\) から \(0\) になるような繰り上がりは、連続する数のなかで高々 \(1\) 回しか発生しません。 例えば、\(x-1 = 19, x = 20, x+1 = 21\) の場合、繰り上がりが発生するのは \(x-1 \to x\) の \(1\) 回だけです。
したがって、「\(x-1 \to x\)」と「\(x \to x+1\)」の遷移のうち、少なくとも一方は繰り上がりが発生しません。
3. \(K > 1\) の場合
繰り上がりが発生しない遷移を \(y \to y+1\) とします。このとき、 $\(\mathrm{digitsum}(y+1) = \mathrm{digitsum}(y) + 1\)$ が成り立ちます。
もし、これら両方の桁和を \(K\) で割った余りが \(R\) であると仮定すると、 $\(R \equiv R + 1 \pmod K\)\( 両辺から \)R\( を引くと、 \)\(0 \equiv 1 \pmod K\)\( となります。これが成り立つのは **\)K = 1$ のときのみ**です。
したがって、\(K > 1\) のときは、連続する3つの整数の桁和がすべて \(K\) で割って \(R\) 余るような状況は絶対に発生しません。よって、求める個数は \(0\) となります。
4. \(K = 1\) の場合
\(K = 1\) のとき、すべての整数の桁和を \(1\) で割った余りは必ず \(0\)(\(R=0\))になります。 したがって、範囲 \(2 \leq x \leq N - 1\) を満たすすべての整数 \(x\) が条件を満たします。
この範囲にある整数の個数は、 $\((N - 1) - 2 + 1 = N - 2\)$ となります。
アルゴリズム
\(K\) の値による分岐:
- \(K > 1\) の場合:答えは
0です。 - \(K = 1\) の場合:答えは \((N - 2) \bmod (10^9 + 7)\) です。
- \(K > 1\) の場合:答えは
巨大な数 \(N\) の \(\bmod 10^9+7\) の計算: \(N\) は最大で \(5 \times 10^6\) 桁という非常に巨大な数として与えられます。 Pythonでは大きな整数をそのまま扱えますが、巨大な文字列を一度に整数に変換しようとすると、変換処理に \(O(|N|^2)\) の時間がかかり実行時間制限(TLE)になってしまいます。 そこで、文字列を左から順に数桁ずつ(例えば9桁ずつ)に分割し、順次 \(\bmod 10^9+7\) を取りながら累積していくことで、高速に \(N \bmod (10^9+7)\) を計算します。
計算量
時間計算量: \(O(|N|)\) \(N\) の桁数を \(|N|\) とします。\(K > 1\) のときは \(O(1)\)、\(K = 1\) のときは文字列を走査して \(\bmod\) を計算するため \(O(|N|)\) となり、実行時間制限に十分間に合います。
空間計算量: \(O(|N|)\) 入力された文字列 \(N\) をメモリ上に保持するための空間計算量です。
実装のポイント
大きな整数の型変換の回避: Pythonで
int(N_str)を直接実行すると、桁数が多すぎて処理が非常に重くなります。コード中ではN_str[i:i+9]のように9桁ずつのブロックに分割して計算することで、型変換のオーバーヘッドを劇的に減らしています。負の数の \(\bmod\) 処理: \(N \bmod (10^9+7)\) の計算結果が \(0\) や \(1\) の場合、そこから \(2\) を引くと値が負になります。Pythonの
%演算子は負の数に対しても正の余りを返すため、(val - 2) % MODとするだけで正しく処理されます。ソースコード
import sys
def solve():
# 入力を一度に読み込む
input_data = sys.stdin.read().split()
if not input_data:
return
N_str = input_data[0]
K = int(input_data[1])
R = int(input_data[2])
# K > 1 の場合、連続する3つの整数の桁和が全て K で割って R 余ることはあり得ない
if K > 1:
print(0)
return
# K == 1 の場合、全ての整数の桁和を 1 で割った余りは 0 (R=0) になる
# したがって、2 <= x <= N-1 を満たす全ての x が条件を満たす
# 個数は N - 2
MOD = 10**9 + 7
val = 0
L = len(N_str)
# N が非常に大きいため、9桁ずつに分割して mod 10^9+7 を計算する
for i in range(0, L, 9):
block = N_str[i:i+9]
val = (val * (10**len(block)) + int(block)) % MOD
ans = (val - 2) % MOD
print(ans)
if __name__ == '__main__':
solve()
この解説は gemini-3.5-flash-high によって生成されました。
posted:
last update: