これは当時yukicoderを初めて初の自力で解けない問題だった。
http://yukicoder.me/problems/22
問題
1,5,10,50,100,500円の硬貨が無限にある。
これらを使いM円を支払う方法は何通りあるか。
解法
小さい順にx種類の硬貨を使いM円支払う方法を考える。
M=m+p*500 (m=M%500)とし、x種類の硬貨を使いM円支払う方法をf(x,m,p)とする。
この関数fはpに対し(x-1)次式になる。今回は硬貨が6種類なので5次式。
よって3000円程度までDPで組み合わせを求めておけば、p=0~5の時のf(x,m,p)がわかるので、あとはラグランジュ補間でf(x,M%500,M/500)を答えればよい。
ll mo=1000000009; ll dp[100501]; ll rev(ll a) { ll r=1, n=mo-2; while(n) r=r*((n%2)?a:1)%mo,a=a*a%mo,n>>=1; return r; } ll dodo(ll V) { int i,x; ll quo=V/500, rem=V%500, ret=0; FOR(i,6) { ll a=1,b=1; FOR(x,6) if(i!=x) a=a*(mo-((quo-x)%mo))%mo; FOR(x,6) if(i!=x) b=b*(mo-(i-x))%mo; ret+=dp[rem+i*500]*a%mo*rev(b)%mo; } return ret%mo; } void solve() { int i,j,k,l,r,x,y; string s; FOR(i,100400) dp[i]=1; for(i=5;i<=100000;i++) dp[i]=(dp[i]+dp[i-5])%mo; for(i=10;i<=100000;i++) dp[i]=(dp[i]+dp[i-10])%mo; for(i=50;i<=100000;i++) dp[i]=(dp[i]+dp[i-50])%mo; for(i=100;i<=100000;i++) dp[i]=(dp[i]+dp[i-100])%mo; for(i=500;i<=100000;i++) dp[i]=(dp[i]+dp[i-500])%mo; int T; ll V; cin>>T; while(T--) cin>>V, cout<< dodo(V) << endl; }
まとめ
この時ラグランジュ補間を練習したおかげでARC033のDが解けたね。