CF768D Jon and Orbs
题目传送门
思路
很明显是概率 \(dp\)。
状态设计
由于所要求的与【天数】和【所拿物品种类】相关,所以设 \(dp_{i, j}\) 表示在第 \(i\) 天拿完后,已经拿了 \(j\) 中物品。
状态转移
考虑如何在前 \(i\) 天得到 \(j\) 中物品:
- 在前 \(i - 1\) 天,我们可以就已经拿了 \(j\) 种物品,所以我们第 \(i\) 天拿的物品,就必须是已经有的 \(j\) 种中的一个,那么拿到已有物品的概率就是 \(\frac{j}{n}\);
- 在前 \(i - 1\) 天,我们可以只拿 \(j - 1\) 种物品,然后在第 \(i\) 天再拿新的一种,拿到新的的概率是 \(\frac{n - j + 1}{n}\)。\(\\\)
所以就会有如下转移方程:
\[dp_{i, j} = dp_{i - 1, j} \times \frac{j}{n} + dp_{i - 1, j - 1} \times \frac{n - j + 1}{n}
\]
边界条件
在第一天取到一种物品的概率为 \(100\%\),所以 \(dp_{1,1} = 1\),其他的均为 \(0\)。
答案
我们可以先预处理 \(10^4\) 天内的 \(dp\) 值,查询时用二分找到第一个 \(dp_{i, n} \geq \frac{p}{2000}\) 的 \(i\),即是答案。
复杂度
空间 \(O(10^4 \times n)\),时间 \(O(10^4 \times n + q \times log_{2}(10^4))\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 7;
int n, q;
double dp[10007][maxn], p;
int main() {
scanf("%d%d", &n, &q);
dp[1][1] = 1.0;
for (int i = 2; i <= 1e4; ++i) {
for (int j = 1; j <= min(n, i); ++j) {
dp[i][j] = dp[i - 1][j] * 1.0 * j / n +
dp[i - 1][j - 1] * 1.0 * (n - j + 1) / n;
}
}
while (q--) {
scanf("%lf", &p), p /= 2000;
int l = 1, r = 10000, ans = 0;
while (l <= r) {
int mid = (l + r) >> 1;
if (dp[mid][n] - p >= 0) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%d\n", ans);
}
return 0;
}

浙公网安备 33010602011771号