HDU dice DP求期望+推公式

时间:2021-09-27 08:13:08

题意:
一个m边形的骰子,求连续投出n个相同的面,和m个两两不同的面的期望次数。
solution:
\(f_i\)表示已经连续投出i个相同的面,到连续投出n个还需要的期望次数.

\(g_i\)类似的表示第二种问题的期望次数。

对于\(f_i\) ,有两种情况:

① 投出了和前i个相同的面,转移到了\(f_{i+1}\) ,那么\(f_i+=(f_{i+1}+1)*\frac{1}{m}\)

② 投出了一个不同的面,转移到了\(f_1\),那么\(f_i+=(f_1+1)*\frac{m-1}{m}\)

综上,\(f_i=(f_{i+1}+1)*\frac{1}{m}+(f_1+1)*\frac{m-1}{m}=f_{i+1}*\frac{1}{m}+f_1*\frac{m-1}{m}+1\)

继续整理得到:\(m*f_i=m+(m-1)*f_1+f_{i+1}\)

同理的话有:\(m*f_{i+1}=m+(m-1)*f_1+f_{i+2}\)

两式相减得到:\(m*(f_{i+1}-f_i)=f_{i+2}-f_{i+1}\)

可以发现,这成一个等比数列:
\[ f_0-f_1=1\\ f_1-f_2=m\\ …\\ f_{n-1}-f_n=m^{n-1} \]
运用等比数列求和公式可得:\(ans=\frac{m*(1-m^{n-1})}{1-m}+1\)

对于\(g_i\)

① 投出了和前i个都不同的面,转移到了\(g_{i+1}\) ,那么\(g_i+=(g_{i+1}+1)*\frac{m-i}{m}\)

② 投出了以前出现过的面,那么可能转移到了\(g_1,g_2,…g_i\),那么\(g_i+=\sum_{1≤j≤i}{(g_j+1)*\frac{1}{m}}\)

综上,\(g_i=(g_{i+1}+1)*\frac{m-i}{m}+\sum_{1≤j≤i}{(g_j+1)*\frac{1}{m}}=g_{i+1}*\frac{m-i}{m}+\frac{1}{m}*\sum_{1≤j≤i}{g_j}+1\)

同上面\(f_i\)类似的处理之后,可以同样的得到\(g_i-g_{i+1}=\frac{m}{m-i}*(g_{i+1}-g_{i+2})\),直接递推一下即可。

code:

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;

const int N=1e6+1;

int n,m;
DB ans,s[N];

IL int qpow(int x,int p) {
    RG int ans=1;
    for(;p;p>>=1,x*=x)
        if(p&1) ans*=x;
    return ans;
}

int main()
{
    RG int i,T,typ;
    while(scanf("%d",&T)!=EOF) {
        while(T--) {
            scanf("%d%d%d",&typ,&n,&m);
            if(typ==0) printf("%.9lf\n",(DB)n*(1-qpow(n,m-1))/(1-n)+1);
            else {
                ans=0,s[0]=1;
                for(i=1;i<m;++i) s[i]=s[i-1]*n/(n-i);
                for(i=0;i<m;++i) ans+=s[i];
                printf("%.9lf\n",ans);
            }
        }
    }
    return 0;
}