题意:数字生成器随机生成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;
}