CF768D

题意:

\(K\) 种物品,求一次取一件把所有种类取遍的概率不小于 \(p/2000\) 的最小天数。

\(p\)\(Q\) 次询问。

分析:

概率 \(dp\) ,而且选择的顺序没关系,因此二维就可以了

\(dp[i][j]\) 表示第 \(i\) 天,之前已经选择了 \(j\) 种物品的概率。

有两种情况:

  1. \(i\) 天选择在第 \(i-1\) 天前已经选过了的物品的概率
    此时,概率为 \(\frac{j}{n}\)
dp[i][j]+=dp[i-1][j]*j/n;
  1. \(i\) 天选择了没有选过的物品

    概率则是 \(\frac{n-j+1}{n}\) ,表示之前选过 \(j-1\) 个物品,就剩余 \(n-j+1\) 个物品没选择。

dp[i][j]+=dp[i-1][j-1]*(n-j+1)/n;

那么,怎么枚举天数呢?

最极限的情况是:\(1000\) 个物品,需要天数对应的概率不小于二分之一

我们设转移中最高天数为 \(m\)

通过枚举 \(m\) (输出 \(dp[m][1000]\)),发现在这种条件下,需要的天数至少是 \(m=7500\) 天左右,而且转移的时间复杂度符合题意。

因此,我们设 \(m=10000\), 像上面一样进行转移。

最后,我们顺序查找:

\(dp[j][n] \geq p[i]/2000\) ,输出 \(j\) 即可。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5,M=1e3+5;
double dp[N][M];
int K,Q;
int p[N],n,m=10000;
int main(){
    cin>>n>>Q;
    for(int i=1;i<=Q;i++){
        scanf("%d",&p[i]);
    }
    dp[0][0]=1;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            dp[i][j]=dp[i-1][j]*(double)j/n+dp[i-1][j-1]*(double)(n-j+1)/(n);
    for(int i=1;i<=Q;i++){
        int t=1;
        for(int j=1;j<=m;j++) if(dp[j][n]<p[i]*0.0005) t++;
        printf("%d\n",t);
    }
    // cout<<dp[m][n]<<endl;
    system("pause");
    return 0;
}
posted @ 2021-10-10 11:04  Evitagen  阅读(39)  评论(0编辑  收藏  举报