Typical DP Contest_E_数
コンテストの名前の通りDPの問題.どのようにDPテーブルを更新するかが少し特徴的なのでブログにまとめておく.新規性は1ミリもありません.
問題の説明
以下の正整数であり,十進表記したときの各桁の数の和がの倍数であるものの個数を[tex: mod 1,000,000,007 ]で求めよ.
条件
考えた事
見ての通りがとんでもなく大きい.もちろん毎に判定してcountするような解法が全く持って間に合わないのは火を見るよりも明らかだろう.
この問題に限らず,一般に調べるべきもののサイズが非常に大きいもの*1であるとき,string型に直して各桁について調べるアプローチが典型である.今回もそのようなアプローチを取る.つまり,を最大桁の数とみて,その各々の桁の情報を使って問題を解く方針だ.
今回ならどのように見て行けばいいだろうか.もちろん素直に各桁の数字を決めていって全部試す方法では意味がない.どうすべきか.
例えばであったとして,一々これをと試すのはどう考えても無駄が大きい.これを変えたいのだ.ここでDPの考えが出てくる.先にDPテーブルをどう作ればいいかを見てもらおう.
として,DPテーブルに上の条件で満たしている数の個数を書き入れていくのだ
基本はこの形である.数を上の桁から決定していくように眺めるのだ.この場合だと千の位がかのどちらかで決定する.
イメージとしては配るDPを想像してもらえばいい.上の桁(例えば)を決定していく事で,下の桁の情報が増えていく形だ.
千の位がである時,どうやってもを下回る事が確定しているので,残りの百の位,十の位,一の位での倍数となるものは千の位がとなるものから来ることが分かる.残りの桁の決定でも同様である.
千の位がであるとき,もちろん未満確定なので,
となる.明らかに桁の情報が増えたとしても元を辿れば前の桁の情報からきている.
今回であれば,各桁の合計がDで割り切れるかどうかなので,問題の条件式は各桁の合計として
となる.上の例であるならば千の位はとして進めているので,となる.としているので1桁目はのいずれしか存在しない.つまり,
の形である.
そして,上の桁が確定であるものを,,,,...に配っていくのだ.
ここでは何かというと二桁目,つまり百の位の数字であり,00~09の段階で条件を満たしているものは何個かというように見ている.
今回であると
となる.見て分かるようにさっきまで雑然としていたものが徐々にまとまってきている.
まだ終わりではない.今度は1桁目,つまり千の位が1のものを考える.千の位がであるとき,未満確定かどうかは分からないため,
となる.今度はこれを配っていく.
千の位がであり,を超えないように百の位を決めるとき,百の位の数として考えられる候補はであり,このうちは明らかに未満確定する.
,である事に注意すると,
のように配られ,結果,
となる.最後に,千の位が1,百の位が2であるものを考えると,これは未満確定ではないため,
と配られる.つまり,
となる.以上をまとめると,
となる.これは以下となるよう上2桁を決める時の候補がの計個である事と一致している.
ポイントとなるのは桁数の合計を記録するのではなく,桁数の合計をで取った数を記録している事にある.こうする事で配列の幅をとし,情報の整理と計算量削減に成功している.
乱暴な言い方かもしれないが,であるとき,とととが持つ情報量は全く同じであるから,1括りにまとめておくのが楽だというような解釈をすればいいだろう.これは楽であるだけでなく,こう1括りにまとめてしまう事で,計算量の幅を
から大きく落とし込める点でも有益である.
この纏めるというところが桁DPの真髄かもしれない.まず,前の桁を決定し問題の条件式に応じてまとめ,その次の桁にその情報(個数)を配り,またまとめ,また配りを繰り返していく.
今回であればまとめる作業は[tex:*2\bmod D]であり,これはただ剰余を求めているだけであるからで行える.本来はで一々見て,各々の桁の和について一つ一つ考えるため,計算量はであったところから比べると大きな進歩であろう.これを踏まえてコードに書き起こすと以下の通り.
初期条件として桁目でであるとき,の0桁目はであるとみなして,としている.
後はコードにある通り,上から桁を決め,Dの剰余の回数配り,新しい桁としてあり得るのはであるとしてループを回す.
計算量はとなり,制限時間に間に合う.
答えとしては,,を合計したものからを引いたものとなる.
は桁目*3まで決めて未満確定のもの()で,各桁の合計をで割った余りがとなるものの合計であり,は桁目まで決めて,未満確定でないもの(これは実質のみを指す)の中で,各桁の合計をで割った余りがとなるものの合計である.*4
を引く理由としては初期条件の部分でとしており,それが巡り巡って,の中にの個数1個分を余分に含む形となっているからである.問題文が「以下の非負整数で~」となっていた場合,この処理は行わなくていい.
いかがだっただろうか.正直,自分のプログラミングの力が足りなく,例に大きく頼った未熟な説明となってしまった.これが今の自分の限界である.
とはいえ,今の自分の力で精一杯,桁DPのアルゴリズムを説明できていると思う.これを読んだ誰かの理解の一助となる事を願っております.今回も最後までお読みいただきありがとうございました.