CF768D Jon and Orbs

题目传送门


思路

很明显是概率 \(dp\)

状态设计

由于所要求的与【天数】和【所拿物品种类】相关,所以设 \(dp_{i, j}\) 表示在第 \(i\) 天拿完后,已经拿了 \(j\) 中物品。

状态转移

考虑如何在前 \(i\) 天得到 \(j\) 中物品:

  1. 在前 \(i - 1\) 天,我们可以就已经拿了 \(j\) 种物品,所以我们第 \(i\) 天拿的物品,就必须是已经有的 \(j\) 种中的一个,那么拿到已有物品的概率就是 \(\frac{j}{n}\)
  2. 在前 \(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;
} 
posted @ 2025-04-06 14:05  syzyc  阅读(10)  评论(0)    收藏  举报