AtCoder Beginner Contest 208 E,F問題メモ
最近ABC-Dがむずい。
E - Digit Products
問題
- N 以下の正整数で、各桁の数字の積が K 以下のものの個数を求めよ
- 1≤N≤1018
- 1≤K≤109
解法
問題文が桁DPの形をしている。
- DP[i][k]= 上位 i 桁目までを考えて、下記を全て満たすものの個数
- N より小さいことが確定している
- i 桁目までに0以外の数が出現している
- 各桁の積が k
k が 0~K の範囲を取るので一見計算量的に無理そうだが、 実際は1桁の数の積なので素因数分解すると 2a3b5c7d と表せる数に限られる。 a=0~log2K,b=0~log3K,... の範囲を取るので、多く見積もっても O((logK)4) で収まる。
これとは別に「just: N の上位 i 桁目までの積」も管理しておけば、更新は以下の3つで行える。
DP[i−1] で既に管理されているパターン
上位 i−1 桁までの積が m の時、i 桁目を t=0~9 のいずれにするかで10通りに遷移する。
- mt≤K の時
- そのまま遷移後の積を管理し続ける
- DP[i][mt]+=DP[i−1][m]
- mt>K の時
- 積が K 以下になるには、これ以降の桁に 0 が出てくるしかない
- →0 が出てくるパターンをその場で答えに足し、DPの管理からは除外する
- ans+=f(i)×DP[i−1][m]
- f(i): i+1 桁目以降に0が出てくるパターン数
- 自由に決められる桁は N−i 桁残っている
- 数字の決め方 10N−i 通りのうち、0が出てこないのは全ての桁が1~9である 9N−i 通り
- f(i)=10N−i−9N−i
上位 i−1 桁が N と一致し、i 桁目で初めて小さいことが確定するパターン
DPに新しく追加される。
N の i 桁目が x の時、 DPで管理する数の i 桁目は t=0~(x−1) にすることができる。
上記と同様に遷移する。ただし新規に追加されるパターン数は1通りとなる。
- just×t≤K の時
- DP[i][just×t]+=1
- just×t>K の時
- ans+=f(i)
i 桁目が最上位桁のパターン
DPに新しく追加される。
i≥2 において、t=1~9 の9通りに遷移する。
- t≤K の時
- DP[i][t]+=1
- t>K の時
- ans+=f(i)
最終的に
途中でansに加えられた数と、DPで管理されている数をあわせたものが答え。
ただし、N そのものが各桁の積が K 以下である場合は数えられていないので、 just≤K であれば答えに1足す。
10進数なので各状態からの遷移は D=10 通り ずつあり、計算量は O(logN(logK)4D) となる。
工夫1
定数 INF を K より大きい数(何でもよい)として定義しておく。
mk>K となった場合、遷移先は DP[i][INF]+=DP[i−1][m] として1つの状態にまとめてしまえばよい。
この方法なら f(i) を考える必要が無いため、追加実装が少なく抑えられる。
F - Cumulative Sum
問題
- 非負整数 n,m に対して関数 f(n,m) を正整数 K を用いて以下で定義する
- f(n,m)={0(n=0)nK(n>0,m=0)f(n−1,m)+f(n,m−1)(n>0,m>0)
- N,M,K が与えられるので、f(N,M) を mod109+7 で求めよ
- 0≤N≤1018
- 0≤M≤30
- 1≤K≤2.5×106
解法
制約が N,M,K で意味ありげに異なっていて、特に M が小さい。
問題ページのサンプル1の説明にも表があるが、 n>0,m>0 の部分に関しては、よくある経路数え上げのように「上と左の値を足す」という操作になっている。
で、左端が nK、上端が 0 で初期化される感じ。
K=2 n\m 0 1 2 3 4 0 0 0 0 0 0 1 1 1 1 1 1 2 4 5 6 7 8 3 9 14 20 27 35
左端が特殊なものの、遷移は経路数え上げなので、二項係数が出てきそう。
左端の値毎に表を分離すると以下のようになり、
それぞれの表は二項係数の表(をシフトしたもの)に [ ]
でくくった数をかけたものとなっている。
n\m 0 1 2 3 4 0 0 0 0 0 0 1 [1] 1 1 1 1 2 0 1 2 3 4 3 0 1 3 6 10 n\m 0 1 2 3 4 0 0 0 0 0 0 1 0 0 0 0 0 2 [4] 4 4 4 4 3 0 4 8 12 16 n\m 0 1 2 3 4 0 0 0 0 0 0 1 0 0 0 0 0 2 0 0 0 0 0 3 [9] 9 9 9 9
f(n,m) はその総和なので、以下で表せる。
- f(n,m)=n∑i=1iK×n−i+m−1Cm−1
……が、N が巨大すぎてこんなんまともに計算できない。どうすんの?
ラグランジュ補間
知識ゲー的なところはあるが。
x の D 次多項式 f(x) は、D+1 個のサンプル点 (xi,f(xi)) を与えることで一意に決まる。
それを利用して、f の形を機械的に特定し、 他の x を代入しても答えを求められるようにするアルゴリズムが、ラグランジュ補間になる。
先ほどの f(n,m) は、m=M を固定すると n の多項式として表せる。
まずΣの中の部分は、iK はそのまま i の K 次式、 n−i+M−1CM−1 は展開すると i の M−1 次式になるので、 あわせて K+M−1 次式となっている。
それの和を1から n まで取ると、次数は1個増えて n の K+M 次式となることが証明されている。
従って、K+M+1 個の n に対する f(n,M) を愚直に計算して、ラグランジュ補間すれば答えが求まる。
この時、一般的にラグランジュ補間は O(D2) かかってしまうが、 サンプル点の n を適切に選ぶ(0~K+M の連番にする)ことで、 1つの値を求めるだけなら O(D) で計算できるテクニックがあるので、それを使う。