geam1113’s diary

主にAtcoderのコンテストの備忘録を書きます。AtCoder緑と水色を行ったり来たりしています。

ABC207E Mod i

前に解説読んでよくわからなかったので寝かしていましたが、今回やってみたら解けました。

 

まず、以下に頻出の考え方について説明します。

ある整数列AA_iを末尾とする連続した部分列A_j,A_{j+1},...,A_iの要素の和がMの倍数となるような、全てのjを求めることを考えます。

 

S_k=A_1+...+A_kとすると、

A_j,A_{j+1},...,A_iの和はS_i - S_{j-1}なので、

これがMの倍数であるためには、

S_i - S_{j-1} \equiv 0 \ (mod \ M)

が成り立つ必要があり、したがって、

S_i \equiv S_{j-1} \ (mod \ M)

が成り立つようなjは全て条件を満たします。

 

iを1から順に探索し、その過程で、S_i \ mod\ Mを記録しておけば、O(N)で、全てのA_iを末尾とした数列について計算できます。

 

この考え方を前提とし、本題に移ります。

まず、一例として、問題の条件を満たすようなiを末尾とした3分割の方法の総数を求めてみます。

 

これは前述した考え方を使うと、S_i \equiv S_{j-1} \ (mod \ 3)を満たすようなjの全てで、連続した部分列A_j,A_{j+1},...,A_iの要素の和が3の倍数となります。

 

そのため、A_1,...,A_{j-1}を2分割するような分割の方法全てに対して、A_j,...,A_iの部分列を付加し、3分割された数列を作り出すことができます。

 

そこで、以下のようなDPを考えます。

dp[i][j][k]:=A_iを末尾としたj個に分割された数列であり、S_i \ mod\ (j+1)kであるものの総数

(j個に分割された数列については、次のj+1の余りを保存しなければいけないことに注意)

 

遷移は、各A_iを末尾とする数列をj個に分割する場合、

  • A_x\ (1 \leq x \leq i-1)を末尾とする数列がj-1個に分割されていて、

  かつ、

  • k=S_i\ mod\ jとしたとき、S_x\ mod\ jkであるもの

A_{x+1},...,A_{i}を付加できる(xx+1の間で分割できる)ので、

 

dp[i][j][S_i\ mod \ (j+1)] += dp[1][j-1][k]+...+dp[i-1][j-1][k]


となります。

このままだと、状態数N^3と計算量O(N^3)となります。

 

そこで、

dp[j][k]:=j個に分割された数列であり、「要素の和\ mod\ (j+1)」がkであるものの総数

とすると、総数は累積和的に計算できるようになり、末尾の情報も不要となります。

遷移は、

dp[j][S_i\ mod \ (j+1)] += dp[j-1][k]

 

状態数N^2と計算量O(N^2)となります。

(但し、jは分割の個数の多いほうから更新)

 

この遷移に基づいて、A_{N-1}までの値を計算し、最後にA_Nを考慮したときに更新された分の和をとれば答えとなります。

 

提出コード:https://atcoder.jp/contests/abc207/submissions/25645338