Problem
給定 \(n\) 個長度爲 \(a_i\) 的巧克力,每次以正比於 \(a_i\) 的概率取得一個巧克力,然後在 \((0,a_i)\) 中隨機選擇一個實數 \(r\) 並將其分成 \(r,a_i-r\) 兩個部分放回。
計算使得所有巧克力的長度均小於 \(K\) 的期望操作次數。
\(n\le 50,k,\sum a_i\le 2000\)。
Sol
這裏將每塊巧克力看成線段,長度爲 \(L_i\),線段長度總和爲 \(L\)。
n=1
首先,我們來考慮 \(n=1\) 的情形。
問題變成:給你一條長度爲 \(L\),每次等概率在線段上隨機一個點切斷,求期望次數,使得第一次所有線段長度 \(\le K\)。
現在,假設切 \(n\) 次得到 \(n\) 個點 \(X_{(1)},X_{(2)},\cdots,X_{(n)}\),並且按照從小到大排序得到 \(X_1\le X_2\le\cdots X_n\)。令 \(X_0=0\),按題意,必須滿足
令 \(w=\frac KL\),設 \(z_1=X_1-X_0\),\(z_2=X_2-X_1\),\(\cdots\),\(z_n=X_n-X_1\),我們要求概率
考慮求後面的積分,發現其可以差分,記
則之前的積分可以寫成
現在我們來求 \(F(x)\)。其實 \(\sum_{i=1}^nz_i\) 遵循 Irwin-Hall 分佈,其 累積分佈函數 滿足
我們從 OI 的角度試圖不嚴謹地證明它。首先假設 \(0\le x\le1\),當 \(n=1\) 時,\(F(x)=x\);當 \(n=2\) 時,\(F(x)\) 是二維平面座標系中的直角三角形面積,爲 \(\frac{x^2}2\);當 \(n=3\) 時,是直角四面體體積,爲 \(\frac{x^3}6\cdots\) 不難發現,當 \(x\le 1\) 時,\(F(x)=\frac{x^n}{n!}\)。當 \(x\ge 1\) 時,我們可以通過容斥,每次枚舉哪些 \(z_i\ge 1\),不斷扣掉,直接推出來上式成立。
記 \(p_n\) 表示帶回原式
這裏 \(x=\lfloor\frac1w\rfloor\)。
最終答案根據期望定義,有
利用公式
即可求解。
n 任意
這裏定義第 \(i\) 條線段,切 \(m\) 次得到結果非法的概率爲 \(q_{i,m}\),由上面得到
設 \(p_m\) 表示切 \(m\) 刀 不合法 的概率,則有
記 \(Q_i(x)\) 表示後面部分的 EGF,則
記 \(P(x)\) 表示 \(p_n\) 的 EGF,則
現在詳細推導 \(Q_i(x)\)
由於
所以有
求出來的 \(P(x)\) 是 EGF,而設 \(P^*(x)\) 是 \(p_m\) 的 OGF。通過轉化,我們用 \(\sum_{t,r}a_{t,r}x^{t-r}\exp\left(\left(1-\dfrac KLt\right)x\right)\) 的形式表示 \(P(x)\),其中 \(0\le t\le L, 0\le r\le\min(n,t)\)。現在,我們想要 \(P(x)\rightarrow P^*(x)\),就要着手考慮 \(x^k\exp(Cx)\) 的形式,不難發現
轉變成 OGF:
通過這樣我們就能得到 \(P^*(x)\)。最後答案爲 \(P^*(1)\),直接代值即可。
複雜度 \(\mathcal O(nL^2)\),使用 NTT
可使複雜度降成 \(\mathcal O(nL\log n\log nL)\)。
#include <bits/stdc++.h>
#define pb push_back
using std::vector;
typedef vector<int> poly;
typedef vector<poly> poly2;
const int N = 55, M = 2005, P = 998244353;
int n, K, L, l[N], len;
int inc(int a, int b) { return (a += b) >= P ? a - P : a; }
int qpow(int a, int b) {
int t = 1;
for (; b; b >>= 1, a = 1LL * a * a % P)
if (b & 1) t = 1LL * t * a % P;
return t;
}
int W[M * 2], inv[M], fac[M], ifac[M];
void prework(int n) {
for (int i = 1; i < n; i <<= 1)
for (int j = W[i] = 1, Wn = qpow(3, (P - 1) / i >> 1); j < i; j++)
W[i + j] = 1LL * W[i + j - 1] * Wn % P;
inv[1] = fac[0] = ifac[0] = 1;
for (int i = 2; i <= n; i++)
inv[i] = 1LL * (P - P / i) * inv[P % i] % P;
for (int i = 1; i <= n; i++)
fac[i] = 1LL * fac[i - 1] * i % P, ifac[i] = 1LL * ifac[i - 1] * inv[i] % P;
}
void fft(poly &a, int n, int op) {
a.resize(n);
static int rev[M * 4] = {0};
for (int i = 1; i < n; i++)
if ((rev[i] = rev[i >> 1] >> 1 | (i & 1 ? n >> 1 : 0)) > i) std::swap(a[i], a[rev[i]]);
for (int q = 1; q < n; q <<= 1)
for (int p = 0; p < n; p += q << 1)
for (int i = 0, t; i < q; i++)
t = 1LL * W[q+i] * a[p+q+i] % P, a[p+q+i] = inc(a[p+i], P - t), a[p+i] = inc(a[p+i], t);
if (op) return;
for (int i = 0, inv = qpow(n, P - 2); i < n; i++) a[i] = 1LL * a[i] * inv % P;
std::reverse(a.begin() + 1, a.end());
}
int getsz(int n) { int x = 1; while (x < n) x <<= 1; return x; }
struct Poly {
poly2 a; int n, m;
poly &operator [] (int i) { return a[i]; }
Poly(poly2 &po, int nn, int mm) : a(po), n(nn), m(mm) {}
};
vector<Poly> F;
Poly operator * (Poly a, Poly b) {
poly2 res;
int n = a.n + b.n - 1, m = a.m + b.m - 1, ln = getsz(n), lm = getsz(m);
for (int i = 0; i < a.n; i++) fft(a[i], lm, 1);
for (int i = 0; i < b.n; i++) fft(b[i], lm, 1);
res.resize(n);
for (int i = 0; i < lm; i++) {
poly l, r;
for (int j = 0; j < a.n; j++) l.pb(a[j][i]);
for (int j = 0; j < b.n; j++) r.pb(b[j][i]);
fft(l, ln, 1), fft(r, ln, 1);
for (int j = 0; j < ln; j++) l[j] = 1LL * l[j] * r[j] % P;
fft(l, ln, 0);
for (int j = 0; j < n; j++) res[j].pb(l[j]);
}
for (int i = 0; i < n; i++) fft(res[i], lm, 0), res[i].resize(m);
return Poly(res, n, m);
}
int main() {
scanf("%d%d", &n, &K);
for (int i = 1; i <= n; i++)
scanf("%d", &l[i]), L += l[i];
len = L + 1; prework(len);
for (int i = 1; i <= n; i++) {
poly tmp0, tmp1; poly2 tmp;
for (int j = 0; j <= l[i] / K; j++) {
int t0, t1;
if (l[i] == K * j) t0 = t1 = 0;
else {
t0 = 1LL * (j & 1 ? P - 1 : 1) * ifac[j] % P * qpow(1LL * (l[i] - j * K) * inv[L] % P, j) % P;
if (j) t1 = 1LL * (j & 1 ? P - 1 : 1) * ifac[j - 1] % P * qpow(1LL * (l[i] - j * K) * inv[L] % P, j - 1) % P; else t1 = 0;
}
tmp0.pb(t0), tmp1.pb(t1);
}
F.pb(Poly(tmp = {tmp0, tmp1}, 2, l[i] / K + 1));
}
for (int i = 1; i < n; i <<= 1)
for (int j = 0; j < n - i; j += i * 2)
F[j] = F[j] * F[j + i];
int ans = 0;
for (int i = 0; i < F[0].n; i++)
for (int j = 0; j < F[0].m; j++) {
if (j < i || j == 0 || K * j == L) continue;
ans = (ans + 1LL * fac[j - i] * qpow(L, j - i + 1) % P * qpow(qpow(1LL * j * K % P, P - 2), j - i + 1) % P * F[0][i][j]) % P;
}
printf("%d\n", ans ? P - ans : 0);
return 0;
}