解き方のアプローチは面白いけど、問題設定はちょっと強引か。
http://codeforces.com/contest/441/problem/E
問題
数値X,K,Pが与えられる。
ここで以下の処理をK回行う。
- 乱数を引き、P%以下の確率でXを2倍する。
- それ以外の場合、すなわち(100-P)%の確率でXを1インクリメントする。
最終的なスコアは、Xを2進数表記したとき、末尾に連続した0の数である。
最終的なスコアの期待値を求めよ。
解法
基本的にはXを2倍することでスコアが1ずつ増えていく。
しかし、1インクリメントを繰り返しても末尾に0が続く場合がある。
とはいえインクリメントは高々K回である。
ここで自力でうまく解けなかったので、Editorialを見て解答。
そこで状態として[処理回数][末尾8bit][9bit目の値][9bit目の値が上にいくつ連続するか]を持ってDPすればよい。
2倍するにしても1インクリメントするにしても、末尾8bitから繰り上がった分が9bit目に行くので、9bit目の値やその連続回数を更新していけばよい。
int X,Y,K,P; double pp; double dpdp[201][256][2][300]; void solve() { int f,i,j,k,l,x,y,z; cin>>X>>K>>P; pp=P/100.0; if(X<256) dpdp[0][X][0][0]=1; else { i=1; x=X>>8; y=x%2; x>>=1; while(x%2==y) i++,x>>=1; dpdp[0][X%256][y][i]=1; } FOR(i,K) { FOR(x,256) FOR(y,2) FOR(z,300) { // x2 if(y==(x>>7)) dpdp[i+1][(x<<1)%256][y][z+1] += dpdp[i][x][y][z]*pp; else dpdp[i+1][(x<<1)%256][y^1][1] += dpdp[i][x][y][z]*pp; // add if(x==255 && y==0) dpdp[i+1][0][1][1] += dpdp[i][x][y][z]*(1-pp); else if(x==255 && y==1) dpdp[i+1][0][0][z] += dpdp[i][x][y][z]*(1-pp); else dpdp[i+1][x+1][y][z] += dpdp[i][x][y][z]*(1-pp); } } double ret=0; for(i=1;i<256;i++) { j=i; x=0; while(j%2==0) x++,j>>=1; FOR(y,2) FOR(z,300) ret+=x*dpdp[K][i][y][z]; } FOR(z,300) { ret+=(z+8)*dpdp[K][0][0][z]; ret+=8*dpdp[K][0][1][z]; } _P("%.12lf\n",ret); }
まとめ
珍しい形のDPだな。