题意:数字生成器随机生成n种数(1~n),(1<=n<=3000),已经成了k个数,再给出已经生成的k个数。求使得1~n都至少出现2次,还需要生成次数的期望。有t组数据(1<=t<=1e5)
朴素做法,dp[i][j]:已经由i个数出现了1次,j个数出现了2次,达到目标状态需要的期望。
复杂度O(n*n*t),tle。
船新思路,将状态设置为与n无关,则只需要一遍n*n就可以t输出了,复杂度O(n*n+t);
dp[i][j]:已经由n-i个数出现了1次,n-j个数出现了2次,达到目标状态需要的期望。
转移方程:

只有dp[i][j]前面的系数是(1-x)的形式才可以提n!!!
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 3010;
bool vis1[N],vis2[N];
int cnt1,cnt2;
double dp[N][N];
int n, k;
void init()
{
memset(dp, 0, sizeof(dp));
for(int i = 0; i <= 3000; ++i)
for(int j = i; j <= 3000; ++j)
{
if(i==0 && j==0) continue;
dp[i][j] = 1.0/j;
if(i!=0) dp[i][j] += 1.0*i/j*dp[i-1][j];
if(j!=0) dp[i][j] += 1.0*(j-i)/j*dp[i][j-1];
}
}
int main()
{
init();
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &k);
memset(vis1, 0, sizeof(vis1));
memset(vis2, 0, sizeof(vis2));
cnt1=cnt2=0;
for(int i = 1; i <= k; ++i)
{
int x;
scanf("%d", &x);
if(!vis1[x])
{
vis1[x] = 1;
++cnt1;
}
else if(!vis2[x])
{
vis2[x] = 1;
++cnt2;
}
}
printf("%.9f\n", n*dp[n-cnt1][n-cnt2]);
}
return 0;
}

博客围绕数字生成器问题展开,该问题是求随机生成n种数(1~n),在已生成k个数的情况下,使1~n都至少出现2次还需生成次数的期望。介绍了朴素做法及复杂度,因复杂度高会超时,提出新思路,设置与n无关的状态并给出转移方程。

被折叠的 条评论
为什么被折叠?



