Atcoder Begginer Contest 029_D_1
水色の問題.桁DPの立て方をしっかり学べてよい.
問題の説明
を満たすすべての整数に含まれるの個数はいくつか答えよ.
制約
考えた事
まずの制約に注目したい.上限がであり,をからまで実際に動かしての個数を
数え上げるの解法は厳しく見える.こういった場合に有効なのが桁DP
である.
桁DP
については
garakutagoya.hatenablog.com
でも記載した通りである.をベースに条件を付随する.
今回もそれを使う.こうする事で計算量を桁数まで落とせる.計算量を落とせる理由としてはである場合に,をとして記録するのではなく,「桁目まで見た時に未満確定かつの個数が個」として一緒くたの遷移元として扱えるからである.一般に配列のデータへのアクセスはで行えるため,要素数を減らせばそれだけ計算量を落とせるという算段である.1 という事であらためてDP式はとなる.後は遷移の仕方を考えればいい.
桁DPの遷移のさせ方は動かす数と注目している数との大小関係によって場合分けすると見通しがいい.
rep(i, num.size()) { ll kazu = num[i] - '0'; rep(p, 10) { if (p < kazu) { rep(j, 2) { rep(k, 12) { if (p == 1) { dp[i + 1][1][k + 1] += dp[i][j][k]; } else { dp[i + 1][1][k] += dp[i][j][k]; } } } }
上のコードの断片を見てもらいたい.数の取り方はで通りあるが,それとのそれぞれの桁の値とを順に比較している.2 この場合はがのある桁より小さい時を表している.
この時,この桁より下の桁がどんなものであっても,それが以上にはならない.たとえばの時,となる数は下二桁が何であってもこれはに到達しないのだ.したがって遷移先はとなる.
についてはの時はの個数がひとつ増えるため,それに気をつければいい.
else if (p == kazu) { rep(j, 2) { rep(k, 12) { if (p == 1) { dp[i + 1][j][k + 1] += dp[i][j][k]; } else { dp[i + 1][j][k] += dp[i][j][k]; } } }
との注目している桁の数字が同じ時はどうすればいいか.この場合はにさせる力はないため,前の数字に依存する.例を挙げるとのとき,は未満確定だがが未満確定とはならないという事だ.したがって遷移先も前のものがに依存する.
else { rep(k, 12) { if (p == 1) { dp[i + 1][1][k + 1] += dp[i][1][k]; } else { dp[i + 1][1][k] += dp[i][1][k]; } } }
がの注目している桁の数字より大きい場合はどうすればいいか.この時は遷移元を絞る必要がある.のとき,となるものは有り得ない.したがって,となるもののみを考えればいい.このとき明らかに遷移元,遷移先共に未満確定となる.
以上のようにを操作する事で解が得られる.聞かれているものはの個数であるから,答えの計算はを足し合わせるといいだろう.ここまでお読みいただきありがとうございました.
プログラム
#include <bits/stdc++.h> using namespace std; #include <atcoder/all> using namespace atcoder; using ll = long long; using vll = vector<long long>; using vvll = vector<vll>; using vvvll = vector<vvll>; #define rep(i, n) for (ll i = (0); i < (ll)(n); i++) int main() { ll N; cin >> N; string num = to_string(N); vvvll dp(15, vvll(2, vll(15, 0))); dp[0][0][0] = 1; rep(i, num.size()) { ll kazu = num[i] - '0'; rep(p, 10) { if (p < kazu) { rep(j, 2) { rep(k, 12) { if (p == 1) { dp[i + 1][1][k + 1] += dp[i][j][k]; } else { dp[i + 1][1][k] += dp[i][j][k]; } } } } else if (p == kazu) { rep(j, 2) { rep(k, 12) { if (p == 1) { dp[i + 1][j][k + 1] += dp[i][j][k]; } else { dp[i + 1][j][k] += dp[i][j][k]; } } } } else { rep(k, 12) { if (p == 1) { dp[i + 1][1][k + 1] += dp[i][1][k]; } else { dp[i + 1][1][k] += dp[i][1][k]; } } } } } ll ans = 0; rep(j, 2) { rep(k, num.size() + 1) { ans += k * dp[num.size()][j][k]; } } cout << ans << endl; }