CF1477F
CF1477F [* easy]
给定 \(n\) 个长度为 \(a_i\) 的巧克力,每次以正比于 \(a_i\) 的概率取得一个巧克力,然后在 \((0,a_i)\) 中随机选择一个实数 \(r\) 并将其分成 \(r,a_i-r\) 两个部分放回。
计算使得所有巧克力的长度均小于 \(k\) 的期望操作次数。
\(n\le 50,\sum a_i,k\le 2000\)
对于 \(n=1\) 的情况简单推导可以给出答案的算式(令 \(m=a_1\)):
具体的:
仅考虑 \(n=1\),可以发现题目描述的概率可以简单的描述为在长度为 \(m\) 的数轴上切割若干刀,使得相邻两个间隙的距离最大值小于等于 \(k\) 的期望刀数。
一般的,我们转换为期望并计数,答案可以写为 \(\sum P(X\ge i)\),此条件下有这个问题与 \(i+1\) 个随机均匀实数变量 \(\{x_j\}\),满足 \(\sum x_j=m\),其中 \(\max x_i\ge k\) 的概率是一致,转换为 \(1\) 减去 \(x_i\in [0,k]\) 的概率即可。
这个部分只需要考虑 \(n\) 个随机变量满足 \(\sum x_i=m\) 的 “超体积” (不知道准不准确,我大概是这样理解的/yun)即可,是 \(\frac{m^{n-1}}{(n-1)!}\),对于 \([0,k]\) 的限制容斥掉,不难给出答案的算式:
考虑到 \(\frac{1}{(1-x)^{j+1}}\) 的展开形式为 \(\sum \binom{i+j}{j} x^i\),我们可以给出答案为:
考虑 \(n\ne 1\) 的情况,一般化的,我们仍然考虑枚举 \(i\) 并计算 \(P(X\ge i)\)
答案就是:
我们假定每个段分别被操作了 \(i_1,i_2...i_m\) 次,最终被操作了 \(i=i_1+...i_m\) 次,此时对应的概率为 \(\frac{i!}{\prod i_j! (\frac{a_j}{S})^{i_j-1}}\)
对于下半部分,我们考虑对于每个 \(i\) 建立一个关于 \(a_i\) 的指数型生成函数,我们先假定我们将无穷项都存储了起来,那么接下来只需要将这些多项式乘起来即可得到 \(P(X\ge i)\),于是方便起见设 \(m=a_i\),此处与 \(n=1\) 的时候相似:
其中 \(f_i\) 表示操作了 \(i\) 次(对应着 \(i+1\) 个随机变量的情况)但最大值小于等于 \(k\) 的概率。
对于每个 \(i\),记上述多项式为 \(G_i(x)\),令
答案为:
我们可以观察到答案中必然存在一个 \(e^x\) 的项数,其展开形式恰好抵消了 \(+1\) 的影响。
我们考虑 \(f_i x^ie^{zx}\) 在求和式中的贡献可以写为 \(f_i\sum \frac{(i+j)!}{j!}z^j\),等价于 \(f_i\times i!\sum \binom{i+j}{j}z^j\) 这与 \(n=1\) 的形式是一致的,即 \(f_i\times i!\times \frac{1}{(1-z)^{i+1}}\)
其次,可以观察到 \(j\) 与 \(\frac{m-jk}{S}\) 中的 \(j\) 差值至多为 \(1\)(来源于 \(x^{j-1}e^{\frac{m-jk}{S}}\) 这一项),总差值不超过 \(n\) 对此记录并 dp 可以做到 \(\mathcal O(\frac{nL^2}{k})\)
可以使用 NTT 优化至 \(\mathcal O(nL\log L)\) 。
tips:可以注意到 \(e\) 的指标一定是 \(e^{1-\frac{jk}{S}}\),提取系数时的答案刚好是 \(\frac{S^{i+1}}{(jk)^{i+1}}\)
\(Code:\)
#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define mp make_pair
#define pi pair<int, int>
#define pb push_back
#define vi vector<int>
#define int long long
int gi() {
char cc = getchar() ; int cn = 0, flus = 1 ;
while( cc < '0' || cc > '9' ) { if( cc == '-' ) flus = - flus ; cc = getchar() ; }
while( cc >= '0' && cc <= '9' ) cn = cn * 10 + cc - '0', cc = getchar() ;
return cn * flus ;
}
const int N = 52 ;
const int M = 4000 + 5 ;
const int P = 998244353 ;
int n, m, L, S, a[N], f[N][M], g[N][M] ;
int inv[M], fac[M], ifac[M] ;
int fpow(int x, int k) {
int ans = 1, base = x ;
while(k) {
if(k & 1) ans = 1ll * ans * base % P ;
base = 1ll * base * base % P, k >>= 1 ;
} return ans ;
}
void inc(int &x, int y) { ((x += y) >= P) && (x -= P) ; }
void dec(int &x, int y) { ((x -= y) < 0) && (x += P) ; }
signed main()
{
n = gi(), m = gi(), fac[0] = 1 ;
rep( i, 1, 2000 ) inv[i] = fpow(i, P - 2), fac[i] = fac[i - 1] * i % P ;
rep( i, 0, 2000 ) ifac[i] = fpow(fac[i], P - 2) ;
rep( i, 1, n ) a[i] = gi(), S += a[i] ;
f[0][0] = 1 ;
rep( i, 1, n ) {
int lim = (a[i] / m) ; L += lim ;
rep( j, 0, i ) rep( k, 0, L ) g[j][k] = f[j][k], f[j][k] = 0 ;
rep( j, 0, lim ) {
int z = a[i] - j * m, o = z * inv[S] % P, v = ifac[j] * fpow(P - 1, j) % P ;
int zz = fpow(o, j + P - 2) * j % P * v % P ;
rep( k, 0, i ) rep( l, 0, L )
inc(f[k + 1][l + j], g[k][l] * zz % P) ;
int zt = fpow(o, j) * v % P ;
rep( k, 0, i ) rep( l, 0, L )
inc( f[k][l + j], g[k][l] * zt % P) ;
}
} int ans = 0 ;
rep( o, 0, n ) rep( j, 0, L ) {
if((!o) && (!j)) continue ;
if(!f[o][j]) continue ;
int i = j - o ;
int d = f[o][j] * fac[i] % P * fpow(S * fpow(j * m, P - 2) % P, i + 1) % P ;
dec(ans, d) ;
}
cout << ans << endl ;
return 0 ;
}