AtCoder Beginner Contest 115: D - Christmas
問題
問題文
https://beta.atcoder.jp/contests/abc115/tasks/abc115_d
問題概要
多次元バーガーを作る事を考える. 多次元バーガにはレベルがあり, レベル L バーガー (Lは 0 以上の整数) は以下のように定義される.
レベル N (1 <= N <= 50) バーガーの下から X (1 <= X <= レベルNバーガの層の総数) 層までに含まれるパティの枚数を出力せよ.
解答例
指針
解説
以下, パティを P
バンズ を B
とする.
最初に思いつく方針として, レベル 50 までのバーガーの状態を文字列として持ち, 末尾から数えて X 文字までに含まれる P
の数をカウントするというものがあるがこの方針だと TLE となる.
入力例 3 で以下のように X が非常に大きくなることがわかる. このことからこの方針だととても間に合わないし, メモリ制約的にもきついと判断できる.
50 4321098765432109
ところで, レベル L バーガーはその定義より再帰的に決定される. 漸化式っぽく書くと以下のようになる.
ここで, レベル i バーガーの層の総数を BurgerLen[i-1] とし, レベル N バーガーの下から X 枚の層に含まれるパティの枚数を とすると, X に関して場合分けすることで以下のようにかける.
これをそのまま実装すれば, メモ化をしなくても制限時間以内に答えを出力することができた.
- Python3 による実装例
#!/usr/bin/env python3 N, X = map(int, input().split()) burger_len = [1] for i in range(N): burger_len += [ burger_len[i] * 2 + 3 ] def rec(N, X): if X == 1: return 1 if N == 0 else 0 elif 1 < X <= burger_len[N-1] + 1: return rec(N-1, X-1) elif X == burger_len[N-1] + 2: return rec(N-1, burger_len[N-1]) + 1 elif burger_len[N-1] + 2 < X < burger_len[N]: return rec(N-1, burger_len[N-1]) + \ 1 + rec(N-1, X - burger_len[N-1] - 2) elif X == burger_len[N]: return rec(N-1, burger_len[N-1]) * 2 + 1 print (rec(N, X))
- C++ による実装例
int だとオーバーフローするので注意.
#include <iostream> using namespace std; typedef long long ll; ll burger_len[51] = {1}; ll rec(ll N, ll X) { if (X == 1) { return N ? 0 : 1; } else if (1 < X && X <= burger_len[N-1] + 1) { return rec(N-1, X-1); } else if (X == burger_len[N-1] + 2) { return rec(N-1, burger_len[N-1]) + 1; } else if (burger_len[N-1] + 2 < X && X < burger_len[N]) { return rec(N-1, burger_len[N-1]) + 1 + rec(N-1, X - burger_len[N-1] - 2); } else if (X == burger_len[N]) { return rec(N-1, burger_len[N-1]) * 2 + 1; } } int main() { ll N, X; for (int i = 1; i <= 50; i++) { burger_len[i] = burger_len[i-1] * 2 + 3; } cin >> N >> X; cout << rec(N, X) << endl; return 0; }