HDU4652 Dice
这个题的正解是 \(O(n)\) 一次询问的, 主要用到的技巧是数列操作的技巧。
type 0
使用期望 dp, 设 f[i] 表示末尾已有 i 个相同的, 停止所需的期望次数, 所求即为 f[0]。
\[\begin{align}
f[0] &= 1 +f[1] \tag{1}
\\
f[i] &= 1 + \frac 1mf[i+1] + \frac{m-1}{m}f[1] \tag{2}
\\
f[i+1] &= 1 + \frac 1mf[i+2] + \frac{m-1}{m}f[1] \tag{3}
\\
f[n] &= 0\tag{4}
\end{align}
\]
\((2)-(3)\) 得:
\[m*(f[i]-f[i+1]) =f[i+1]-f[i+2]
\]
注意到这是个递推式, 设 \(f'[i] = f[i]-f[i+1]\), 那么 \(f[0] = \sum\limits_{i=0}^{n-1} f'[i]\)(由于 \(f[n]=0\))。
显然 \(f'[0]=1\),于是就可以递推了。
type 1
还是使用期望 dp, 设 f[i] 表示末尾已有 i 个两两不同的, 停止所需的期望次数, 所求还是 f[0]。
\[\begin{align}
f[0] &= 1+f[1]\tag{1}
\\
f[i] &= 1 + \frac{m-i}{m} f[i+1] + \frac 1m \left( f[1]+f[2]+\cdots +f[i] \right)\tag{2}
\\
f[i+1] &= 1 + \frac{m-i-1}{m} f[i+2] + \frac 1m \left( f[1]+f[2]+\cdots +f[i+1] \right)\tag{3}
\\
f[n] &= 0\tag{4}
\end{align}
\]
还是 \((2)-(3)\) 得:
\[\begin{align}
f[i] - f[i+1] &= \frac{m-i}mf[i+1] - \frac{m-i-1}mf[i+2] - \frac 1mf[i+1]
\\
m*f[i] - m*f[i+1] &= (m-i)*f[i+1] - (m-i-1)*f[i+2] - f[i+1]
\\
m*f[i] - m*f[i+1] &= (m-i-1)*f[i+1] - (m-i-1)*f[i+2]
\\
m*(f[i]-f[i+1]) &= (m-i-1)*(f[i+1]-f[i+2])
\end{align}
\]
又是个递推式, 设 \(f'[i] = f[i]-f[i+1]\), 依然有 \(f[0] = \sum\limits_{i=0}^{n-1} f'[i]\)。
显然 \(f[0]=1\), 依然可以递推。
code
hduOJ 厌 getchar type 快读好震撼啊。
#include<cstdio>
#define gc() getchar()
using namespace std;
double calc0(int n,int m)
{
double res = 1, f = 1;
for(int i=1;i<n;++i)
{
f = f * m;
res += f;
}
return res;
}
double calc1(int n,int m)
{
double res = 1, f = 1;
for(int i=1;i<n;++i)
{
f = f * m / (m-i);
res += f;
}
return res;
}
int main()
{
// cout << calc0(5,6);
int T;
scanf("%d",&T);
while(T--)
{
int op,n,m;
scanf("%d%d%d",&op,&m,&n);
if(op==0)
printf("%.9lf\n", calc0(n,m));
else
printf("%.9lf\n", calc1(n,m));
}
return 0;
}