ツバサの備忘録

主に備忘録代わりに精進記録を載せていくつもりです。

ABC164 D - Multiple of 2019

問題
提出コード

解法

ここでは、Sについて下からi桁目の数字をS_{i}と表記します。また、Sの文字列の長さ、すなわち|S|Nとします。
加えて、X2019で割った余りをX \ \%\  2019と表記します。
まず、Sについて、桁ごとに分解して表現してみると、
S_{1} \times 1 + S_{2} \times 10 + S_{3} \times 100 + ... S_{N} \times 10^{N-1}
となります。
S \% 2019は、次のように書くこともできます。
(S_{1} \times 1 \ \% \ 2019+ S_{2} \times 10  \ \% \ 2019+ ... S_{N} \times 10^{N-1}\  \% \ 2019)\  \% \ 2019
一般化すると、
 \displaystyle ( \sum_{i = 1}^{N} (S_{i} \times 10^{i-1} \ \% \  2019) ) \ \% \ 2019
ここで、Sum[k] = \displaystyle ( \sum_{i = 1}^{k} (S_{i} \times 10^{i-1} \ \% \  2019) ) \ \% \ 2019
と定義してあげると、
Sum[0] = 0,Sum[k] = (Sum[k-1] + (S_{k} \times  10^{k-1}) \ \% \ 2019) \ \% \ 2019

10^{k} \ \% 2019  = (10^{k-1} \ \% \ 2019) \times 10 \ \% \ 2019
の二つを組み合わせ、累積和のようにしてO(N)で計算することができます。
ここで、Si桁目からj桁目までを取り出した数(  i \leqq j )は、これをPとして、
 \displaystyle P =  \frac{\sum_{k = 1}^{j} (S_{k} \times 10^{k-1}) -  \sum_{k = 1}^{i-1} (S_{k} \times 10^{k-1})}{10^{i-1}}
Pが2019の倍数となるには、
P \ \% \ 2019 = 0
となっていればよいです。
2019と10^{X} は互いに素なので、先ほどの数式のうち分子のみに着目することで、結局
 (\sum_{k = 1}^{j} (S_{k} \times 10^{k-1}) -  \sum_{k = 1}^{i-1} (S_{k} \times 10^{k-1}) ) \ \% \ 2019 = 0
であれば、Pは2019の倍数ということがわかります。
これをさらに変形すると、
 (\sum_{k = 1}^{j} (S_{k} \times 10^{k-1} \ \% \ 2019)\ \% \ 2019 -  \sum_{k = 1}^{i-1} (S_{k} \times 10^{k-1}\ \% \ 2019)  \ \% \ 2019) = 0
となり、結局
 \sum_{k = 1}^{j} (S_{k} \times 10^{k-1} \ \% \ 2019)\ \% \ 2019 =  \sum_{k = 1}^{i-1} (S_{k} \times 10^{k-1}\ \% \ 2019)  \ \% \ 2019
という条件であるようなi,jのペアを探せばよいということがわかりました。
さて、この式は先ほど作成したSum[k]そのものの式で、
Sum[i] = Sum[j]
であるようなi,jのペアを探せばよいことになります。
Cnt[k] = Sum[x] = kとなるようなxの個数、という配列を用意しておき、

  1. Sum[i]を計算

  2. Cnt[Sum[i]]を答えに加算

  3. Cnt[Sum[i]]に1加算

という操作を、i = 0から繰り返して行けば、答えが求まります。Cntについては、取り得る添え字の値が[0,2019)なので、そのサイズだけ確保しておけばよいです。

おまけ

今回のように、SumCntを計算しながら答えを求めていく、というものはほかにもいくつかあります。
Cntは、今回添え字が最大で2019にしかならないのであらかじめ配列を確保しておくことができましたが、そうでない場合でも、列の長さがN以下のとき、種類も最大でN通りにしかならないことを利用すると、map等を駆使することで計算ができます。
Sumも、単純な累積和でいいもの等があります。
ほかにも、201910と互いに素でないケースが存在する問題等ありますので、是非見てみると良いと思います。
類題リスト↓

A - Zero-Sum Ranges

AGCの200点でかなりの難関といわれた問題です。しかし、今回の問題を知っていれば、おそらく解けると思います。今回のような操作をする問題の解法ツイート等では、この問題名がよくあがります。

E - Divisible Substring

コーナーケースに注意しましょう。

E - Rem of Sum is Num

少しトリッキーですが、帰着させることができます。

D - Candy Distribution