Codeforces 217B Blackboard Fibonacci
問題
黒板に二行に数字を書く。
最初、上の行に0, 下の行に1を書いた後で次の操作を繰り返す。
- Tの操作 上の行の数字 := 上の行の数字 + 下の行の数字 と書き換える。
- Bの操作 下の行の数字 := 上の行の数字 + 下の行の数字 と書き換える。
これをn回行った結果、最後にrの数字が書かれた。
操作はTBTBTB...と行うはずであったが、何度か間違えて、連続して同じ操作を行った。
間違った回数の最小値および、そのときの操作の列をどれか一つ出力せよ。
(間違いの回数は、同じ操作を連続して行った回数 + 最初がTならさらに1回)
制約条件
n, r≦10^6
方針
最後の状態は(r, x)または(x, r).
最後の状態を一通り決めると、直前の状態は一意に定まるので、
最後の状態を2*r通り全通り決め付けて、最初の状態(0, 1)に戻るかを見ればよい。
一つの状態を(0, 1)に戻すには、互除法と同じ計算量のO(log r)かかるので、全体でO(rlog r)で求まる。
計算できたら解を復元すればいい。
なんか書き方によってはコーナーケースができるっぽいので注意する必要がある。
ソースコード
int n, r; int calc(int t, int b){ int res = 0, step = 0; while(t > 0 && b > 0){ if(t <= b){ res += b / t - 1; step += b / t; b %= t; } else{ res += t / b - 1; step += t / b; t %= b; } } if(max(t, b) != 1 || min(t, b) != 0 || n != step) return inf; if(t == 1) res--; return res; } int main(){ cin >> n >> r; int best = inf, t = -1, b = -1; string ans; if(n == 1 && r == 1){ cout << "0\nT"<< endl; return 0; } rep(i, r){ int tmp = calc(i, r); if(tmp < best) best = tmp, t = i, b = r; tmp = calc(r, i); if(tmp < best) best = tmp, t = r, b = i; } if(best == inf){ cout << "IMPOSSIBLE" << endl; return 0; } while(t > 0 && b > 0){ if(t < b){ rep(i, b / t) ans += 'B'; b %= t; } else{ rep(i, t / b) ans += 'T'; t %= b; } } reverse(all(ans)); if(t == 1) ans[0] = 'T'; cout << best << endl; cout << ans << endl; return 0; }