2020年8月17日月曜日

CodeChef August Challenge 2020 - Subsequence Frequency Counting

数列(Ai)(A_i)が整数xxcxc_x個含むとする。また、整数xxが代表になるような部分列でちょうどkk個のxxを含むものの総数をf(x,k)f(x, k)とする。f(x,k)f(x, k)を二項係数で表すと、以下のようになる:

f(x,k):=(i=0k1(c1i)...i=0k1(cx1i))(cxk)(i=0k(cx+1i)...i=0k(cNi)) f(x, k) := \biggl( \sum_{i=0}^{k-1} \binom{c_1}{i}...\sum_{i=0}^{k-1}\binom{c_{x-1}}{i} \biggr) \binom{c_x}{k} \biggl(\sum_{i=0}^{k}\binom{c_{x+1}}{i} ... \sum_{i=0}^{k}\binom{c_N}{i} \biggr)

f(x,k)f(x, k)NN通りの引数について足せばよいが、毎回すべての二項係数を掛け算していては間に合わない。そこで、dp[x]:=i(cxi)\operatorname{dp}[x] := \sum_i \binom{c_x}{i}という形の列をセグメント木で持っておくことを考える。dp\operatorname{dp}のそれぞれの値が適切なkkに対応していれば、f(x,k)f(x, k)dp[1...x1]\operatorname{dp}[1...x-1]の積と (cxk)\binom{c_x}{k}dp[x+1...N]\operatorname{dp}[x+1 ... N]の積を掛けて求まることになる。k=0k=0からffを求めていくとして、dp\operatorname{dp}の末尾からkkが増えていくので、kkの昇順、タイブレークはxxの降順にf(x,k)f(x, k)を求めつつdp\operatorname{dp}を更新していくと、一回の更新がO(logN){O}(\log N)でできる。