D. Jon and Orbs (概率dp)


题意:给定 k k k种龙晶, q q q个询问,每天产生一种龙晶,产生每种龙晶概率相等,每个询问给定 p p p,求产生 k k k种龙晶的概率 ≥ ( p − ε ) 2000 \ge \dfrac{(p-\varepsilon)}{2000} 2000(pε) 的最小天数。


思路:概率 d p dp dp,令 d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i天产生 j j j种龙晶的概率,有转移方程: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] × j k + d p [ i − 1 ] [ j − 1 ] × k − ( j − 1 ) k dp[i][j]=dp[i-1][j]\times \dfrac{j}{k}+dp[i-1][j-1]\times \dfrac{k-(j-1)}{k} dp[i][j]=dp[i1][j]×kj+dp[i1][j1]×kk(j1)

因为天数不确定,可开足够大的第一维,或者滚动数组去掉第一维,里层循环倒着转移即可。

然后每次求出第 i i i天的情况后,预处理出 a n s [ p ] ans[p] ans[p]数组。

需要注意的是 d p [ i ] [ 0 ] = ( i = = 0 ) 1 : 0 dp[i][0]=(i==0)1:0 dp[i][0]=(i==0)1:0


代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
int k,q;
double dp[N];
int a[N];
int main(){
	scanf("%d%d",&k,&q);
	dp[0]=1;
	for(int i=1,p=1;p<=1000;i++){
		for(int j=k;j;j--)
			dp[j]=dp[j]*j/k+dp[j-1]*(k-j+1)/k;
		while(p<=1000&&dp[k]*2000>=p-1e-7) a[p++]=i;
		dp[0]=0;
	}
	while(q--){
		int p;scanf("%d",&p);printf("%d\n",a[p]);
	}
	return 0;
}