Open1

ABC367 D - Pedometer復習

junjun

問題設定

円形の場所にN 個の休憩所がある
各休憩所間の距離

A_1, A_2, \dots, A_N

i 番目の休憩所から i+1 番目の休憩所までの距離は A_i 歩かかる(休憩所N+1は休憩所1を指す)

休憩所sからtまで行くのにかかる最小の歩数はMの倍数

\sum_{i=s}^{t-1} A_i \equiv 0 \ (\text{mod}\ M)

これを満たす休憩所の組 (s, t) の個数を求めることが目標

#  (N: 休憩所の数, M: 割る数)
N, M = map(int, input().split())

# (A: 各休憩所間の距離)
A = list(map(int, input().split()))

# R配列の初期化 (累積歩数の余りを格納する配列)
# 0から2N-1までの累積歩数の余りを格納するためにサイズ2Nの配列を作る
R = [0] * (2 * N)

# 累積歩数の余りを計算して、R配列に格納する
for i in range(1, 2 * N):
    # 前回の累積歩数の余りに次の休憩所までの距離を加えて M で割った余りを求める
    R[i] = (R[i - 1] + A[(i - 1) % N]) % M

# B配列の初期化 (各余りが何回出現するかを記録する配列)
# Mで割った余りが0からM-1までの数値になるので、サイズMの配列を作る
B = [0] * M

# カウントを初期化 (条件を満たす (s, t) の組み合わせの数)
count = 0

# 最初のN個の余りについて、余りが何回出現するかを記録する
for i in range(N):
    B[R[i]] += 1

# i=Nから2N-1までの範囲で余りを比較し、組み合わせを数える
for i in range(N, 2 * N):
    # B[R[i - N]] を減算 (i - N の範囲を外す)
    B[R[i - N]] -= 1
    # 現在の R[i] と同じ余りが過去に出現している場合、その組み合わせをカウントに加える
    count += B[R[i]]
    # B[R[i]] を加算 (i の範囲に R[i] の余りを追加する)
    B[R[i]] += 1

print(count)

フロー

  1. R配列の計算:

    • R 配列は累積歩数の余りを保存します。2N個の要素を持つ配列を準備し、各休憩所の累積距離を ( M ) で割った余りを順に計算して格納します。
  2. B配列の準備:

    • B 配列は各余りが何回出現したかを記録します。余りが ( 0 ) から ( M-1 ) までの範囲に収まるので、長さ ( M ) の配列を用意します。
  3. 条件を満たす組み合わせのカウント:

    • i = N から i = 2N-1 までの間に、過去に同じ余りが出現していたかをチェックし、その場合は条件を満たす組み合わせとしてカウントに加えます。
    • 各ループで、範囲から外れた過去の余りを B 配列から除き、新しい余りを B に追加します。