10問目に突入、ぼちぼち終盤。
http://autumn_fest.contest.atcoder.jp/tasks/autumn_fest_10
スクロールしていく窓の範囲で忍者が移動するので、窓から外れないような移動順の組み合わせを答える問題。
本番はsmallしか解けなかったので復習。
small
まずは本番も解けたsmallから。
H<=22、T<=22222と窓が小さく、1回のジャンプでの移動距離DはD-√D<=Hを満たさないといけないので最大D=27。
時刻Tの各タイミングで、距離1~27のジャンプを行う組み合わせをDPで足していく。
これはO(TH^2)なので、medium以降は無理。
int H,T; int RT[101]; int MO=1000003; int dp[22230][28]; void solve() { int x,y,i,j,p,rr,TM,TTC,dep,res,t; for(i=j=1;i<=100;i++) { if(j*j<=i) j++; RT[i]=j-1; } GET2(&H,&T); if(H>22 || T>22222) return; ZERO(dp); dp[0][0]=1; res=0; H--; FOR(t,T+1) { FOR(x,H+1) { if(dp[t][x]==0) continue; res = (res + dp[t][x]) % MO; for(y=1;y<=30;y++) { int nx = x+y-RT[y]; if(nx>H) break; if(t+RT[y]>T) break; dp[t+RT[y]][nx] = (dp[t+RT[y]][nx] + dp[t][x]) % MO; } } } _P("%d\n",res); }
large
Medium以降はTが大きいので、O(T)以上の処理は行えない。
幸いH<=77とTは小さいので、こちらを利用する。
この問題では、消えない限りは毎回最低距離1をジャンプしなければいけないので、位置が戻ることはない。
よって、距離2以上をジャンプ(√D分窓がスクロールするので、実質移動距離はD=2の時1)するのは高々77回しか行えない。
そこで、距離2以上のジャンプのパターンをまずはDPで列挙する。
状態としては、経過時間・ジャンプ回数・位置の3通りで、経過時間がTを超えず、位置がHを超えない範囲でDPを行う。
このDPはO(T^3)時間かかるが、Tが小さいので問題ない。
次に、各経過時間・ジャンプ回数ごとに、途中で消えたり距離1のジャンプが入る場合の組み合わせを考える。
距離2以上のジャンプについて、経過時間がX、ジャンプ回数がYとなる組み合わせ数をZすると、距離1のジャンプは0~T-X回可能であり、その間にY回距離2以上のジャンプをするので、その数は回になる。
Combinationの計算は、Yが小さいのと答えがmodを取った値を出せばよいので、1~Yの逆元を求めておけばO(Y)で行えるが、それでもO(TY)かかってしまう。
ここで少し考えてみたのだが、と置けることに気づいた。
よってこのCombinationはO(Y)で計算できる。
int H,T; int RT[101]; ll MO=1000003; ll mod=MO; ll dp[101][101][101]; ll dp2[101][101]; ll combi(ll N_, ll C_) { int i; const int num=100; static ll rev[num+1],revt[num+1]; if(rev[1]==0) { rev[1]=1; for(i=2;i<=num;i++) rev[i]=rev[MO%i]*(MO-MO/i)%MO; revt[0]=1; for(i=1;i<=num;i++) revt[i]=revt[i-1]*rev[i]%MO; } ll ret=revt[C_]; FOR(i,C_) ret = (ret * (N_-i))%MO; return ret; } void solve() { int x,y,z,i,j,p,rr,TM,TTC,dep,res,t; ll ret; for(i=j=1;i<=100;i++) { if(j*j<=i) j++; RT[i]=j-1; } GET2(&H,&T); ZERO(dp); dp[0][0][0]=1; // x:経過時間 y:ステップ数 z:位置 FOR(x,90) FOR(y,90) FOR(z,H+1) { if(dp[x][y][z]==0) continue; for(j=2;j<100;j++) { if(x + RT[j] > T) break; if(z + j - RT[j] >= H) break; dp[x+RT[j]][y+1][z + j - RT[j]] = (dp[x+RT[j]][y+1][z + j - RT[j]] + dp[x][y][z]) % MO; } } //位置はどこでもいいので畳み込み ZERO(dp2); ret = 0; FOR(x,90) FOR(y,90) FOR(z,90) dp2[x][y] = (dp2[x][y]+dp[x][y][z])%MO; FOR(x,90) FOR(y,90) if(dp2[x][y]) ret = (ret + combi(T-x+y+1,y+1)*dp2[x][y]) %MO; _P("%lld\n",ret); }
まとめ
解説はやけに難しいけど、DPをちゃんと行うとそこまで難しくない問題。
最後のCombinationのという特性、一般的なのかな?
自分はこれ知らなくて自分で考え付いたけど、上位の人はさらっとこれ書いてるんだよな…。