题解:P6944 [ICPC 2018 WF] Gem Island
先刻画随机分裂的过程。发现操作序列是随机的,第 \(i\) 天宝石增加前总共有 \(n + i - 1\) 颗宝石,在其中随机选择一颗进行分裂,于是总情况数为 \(n ^ {\overline{d}}\)。
再考虑最终方案对应的操作序列数。记 \(a_i\) 为第 \(i\) 个人 \(d\) 天后的宝石数。
先钦定每天获得宝石的人,是多重集排列数 \(\binom{d}{a_1 - 1, a_2 - 1, \cdots, a_n - 1}\);再考虑一个人获得宝石的过程,当它有 \(i\) 颗宝石时,它的下一颗宝石生成的方案数为 \(i\),于是总方案数为 \((a_i-1)!\)。
乘到一起发现抵消了!每个可能的 \(a\) 对应的方案数均为 \(d!\),因为操作序列随机,所以每个 \(a\) 的概率是均等的 \(\frac{d!}{n ^ {\overline{d}}}\)。
记 \(F(a)\) 为序列 \(a\) 中前 \(k\) 大的值,答案即为:
问题转换为求 \(\sum_{a} F(a)\),\(a\) 满足 $\sum_{i} a_i = n + d $ 且 \(a_i \ge 1\) 。转换一下,记 \(b_i = a_i - 1\),那么 \(b\) 满足 \(\sum_{i} b_i = n\) 且 \(b_i \ge 0\),这是经典问题。
记 \(f_{i,j}\) 为将 \(j\) 有序拆分为 \(i\) 个非负数的方案数,\(g_{i,j}\) 为 \(f_{i,j}\) 对应的所有情况的前 \(r\) 大数的和之和,转移枚举有多少正整数即可。
\(b\) 的答案求出来了,又因为 \(a_i = b_i + 1\),答案即为 \(\frac{g_{n, d}}{f_{n,d}} + r\)。用 long double 存储。\(O(n^2d)\)。
const int N = 505;
int n, d, r; ld C[N][N], f[N][N], g[N][N];
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> d >> r;
for(int i = 1; i <= n; i++){
f[i][0] = C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j++) C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
}
for(int i = 1; i <= n; i++) for(int s = 1; s <= d; s++){
for(int j = 1; j <= min(i, s); j++){
f[i][s] += f[j][s - j] * C[i][j];
g[i][s] += (g[j][s - j] + f[j][s - j] * min(j, r)) * C[i][j];
}
}
cout << fixed << setprecision(7) << ((ld)g[n][d] / f[n][d] + r) << endl;
return 0;
}

浙公网安备 33010602011771号