D - Limestone Editorial
by
potato167
より高速な解
\(H < W\) であるとき、時間計算量 \(O(H^{2} + H \log(W))\) で解けます。
想定解を高速化することで、この計算量を達成できます。
想定解を C++ で書いたものがこちらです。
dpC が A の場合の数、 dpS が \(\sum A\) の総和に対応しています。
これを、Binomial を使わず、式変形をして、階乗の逆数のみを用いて書いたものがこちらです。
一部抜粋すると、以下のようになります。
void solve(){
dpC[0] = 1;
rep(rp, 0, H){
vector<mint> n_dpC(W + 1), n_dpS(W + 1);
rep(i, 0, W + 1){
rep(j, i, W + 1){
n_dpC[j] += dpC[i] * invfact[j - i + 1] * (j + 1);
n_dpS[j] += dpS[i] * invfact[j - i + 1] * (j + 1);
n_dpS[j] += dpC[i] * i * invfact[j - i + 2] * (j + 1);
}
}
swap(n_dpS, dpS);
swap(n_dpC, dpC);
rep(i, 0, W + 1) dpS[i] += dpC[i] * (W - i);
}
mint ans = 0;
rep(i, 0, W + 1) ans += dpS[i] * invfact[W - i];
rep(i, 1, W + 1) ans *= i;
cout << ans.val() << "\n";
}
\(x\) の関数 \(f(x), g(x)\) を \(\displaystyle f(x) = \sum x^{k} dpC[k], g(x) = \sum x^{k} dpS[k]\) と定義したとき、dp を更新する操作は、\(f, g\) を微分する操作や、\(x\) の関数をかける操作に対応します。
たとえば、
rep(i, 0, W + 1){
rep(j, i, W + 1){
n_dpC[j] += dpC[i] * invfact[j - i + 1];
n_dpS[j] += dpS[i] * invfact[j - i + 1];
n_dpS[j] += dpC[i] * i * invfact[j - i + 2]
}
}
は、
\(f_{new} = \dfrac{d\left(f(x)\left(e^{x} - 1\right)\right)}{dx}\)
\(g_{new} =\dfrac{d\left(g(x)\left(e^{x} - 1\right)\right)}{dx} + \dfrac{d\left(\left(\dfrac{df(x)}{dx}\right)\left(e^{x} - x - 1\right)\right)}{dx}\)
と表せます。
rep(i, 0, W + 1) dpS[i] += dpC[i] * (W - i);
は、
\(g_{new} = g + W\cdot f(x) - x\dfrac{df(x)}{dx}\)
と表せます。
\(e^{x}\) などをそのまま持つのは扱いにくので、以下の条件を満たす \(X\) の多項式 \(F(X), G_{0}(X), G_{1}(X)\) を定義します。
- \(F(e^{x}) = f(x)\)
- \(G_{0}(e^{x}) + xG_{1}(e^{x}) = g(x)\)
\(H\) 回のループの中では、\(F, G_{0}, G_{1}\) の次数は高々 \(H\) に収まります。
これを用いるとたとえば、
\(f_{new} = \dfrac{d\left(f(x)\left(e^{x} - 1\right)\right)}{dx}\)
は
\(\dfrac{d\left(f(x)\left(e^{x} - 1\right)\right)}{dx} = \dfrac{dX}{dx}\cdot\dfrac{d\left(F(X)\left(X - 1\right)\right)}{dX} = X \cdot (F(X) + F'(X)(X - 1))\)
と変形できることから
\(\displaystyle F_{new}(X) = \sum_{k} ((k + 1)X^{k + 1} - kX^{k})\cdot [X^{k}](F(X))\)
などと表せます。
これらの \(F, G_{0}, G_{1}\) の更新は \(O(H)\) で行えるので、最終的な \(F, G_{0}, G_{1}\) は時間計算量 \(O(H^{2})\) で求められます。
最後の答えを求めるパートである、
mint ans = 0;
rep(i, 0, W + 1) ans += dpS[i] * invfact[W - i];
rep(i, 1, W + 1) ans *= i;
cout << ans.val() << "\n";
は、
\(ans =W! [x^{W}] g(x)e^{x}\)
と表せるので、
\[\displaystyle ans = W![x^{W}] (G_{0}(e^{x}) + G_{1}(e^{x}))e^{x} = \sum_{k}\left((k + 1)^{W}[X^{k}]G_{0}(X) + (k + 1)^{W - 1}[X^{k}]G_{1}(X)\right)\]
となります。これは、時間計算量 \(O(H\log(W))\) で計算できます。
以上で全体の時間計算量は \(O(H^{2} + H\log(W))\) となります。
posted:
last update: