アプローチはすぐ思いついたがバグ修正に手間取った。
http://community.topcoder.com/stat?c=problem_statement&pm=13345
問題
プレイヤーは初期状態で0個の飴と所持金0円を持つ。
毎日プレイヤーは以下の処理を以下の順で行う。
- 所持金がX円増える。
- 飴を1個も持ってなければ、所持金で買えるだけ1個Y円の飴を買う。
- 飴を1個食べる。
Z日目終了後にプレイヤーが持つお金はいくらか。
なお、X>=Yなので飴を食べられないことはない。
解法
- X==Yの時は毎日1個飴を買いお金が0円になる。
- X>=2Yの時は容易である。
- p個の飴を消費している間に2p個以上飴を買うお金が手に入るので、飴を買うのは高々O(logZ)回で済む。
- よって素直に飴を買うシミュレーションを行えばよい。
- X<2Yの時は、飴を買うたびに買える飴の数が増えない。
- よって、次に買える飴の数が増えるのはいつか、という観点でシミュレートすればよい。
- たとえば今毎回1個しか飴を買えない場合、飴を買うたびに(X-Y)円ずつ所持金が増えるのでY/(X-Y)日立てば2個ずつ飴を買えるようになる。
- 1度に買える飴の数Sが増え、S(X-Y)>Yになると、飴を買える量が毎回増えていくので、あとはX>=2Y同様にシミュレートすれば良い。毎回買える飴の数が少しずつ増えるので、愚直にシミュレートしてもO(√N)で済む。
class CandyAddict { public: ll dodo(ll X,ll Y,ll Z){ if(X==Y) return 0; ll C=0,NC=0; while(X<=2*Y && Z>0) { ll S=(C+X)/Y; ll NS=((C+X)%Y+S*X)/Y; if(NS>S+1) break; // 1 loop inc NC if(S==NS) { ll sd=S*X%Y; ll st=(Y-(C+X)%Y+(sd-1))/sd; if(Z<=st*S) return C+Z*X-(Z+(S-1))/S*S*Y; Z-=st*S; C = C+st*S*(X-Y); } else { if(Z<=S) return C+Z*X-S*Y; Z-=S; C=C+S*(X-Y); } } while(Z>0) { C+=X; NC=C/Y; C%=Y; if(Z<=NC) return C+(Z-1)*X; Z-=NC; C+=X*(NC-1); } } vector<long long> solve(vector <int> X, vector <int> Y, vector <int> Z) { vector<ll> V; int i; FOR(i,X.size()) V.push_back(dodo(X[i],Y[i],Z[i])); return V; } }
まとめ
問題設定がややこしいけど、XとYの大小関係でアプローチが変わるのは面白い。