HDU 4599 概率DP
先推出F(n)的公式:
设dp[i]为已经投出连续i个相同的点数平均还要都多少次才能到达目标状态。
则有递推式dp[i] = 1/6*(1+dp[i+1]) + 5/6*(1+dp[1]).考虑当前这一次掷色子,有1/ 6的概率投的和前面的一样,有5/6的概率不一样,不一样就要重新投,就到了dp[1]的状态,这里投了一次,所以要加1.边界有dp[0] = dp[1]+1,dp[n] = 0;
可以这么说,H[n]应该是6*F[n]的,随便YY一样。
更严谨的话就是一样要去推,递推式如下,设dp[i]为已经连续i次投出1后平均还要多少次才能达到目标状态。
有递推式dp[i] = 1/6*(dp[i+1]+1) + 5/6*(1+dp[0]). (这里和上面的公式不一样)。边界条件dp[0] = 1/6*(1+dp[1]) +5/6*(dp[0]+1).dp[n]=0;逆着推就能直接求出dp[0].
推出来的F(n) = (6^n-1)/5,H(n) = 6*(6^n-1)/5.G(m) = 6*m,平均投6次会出现一次1.
或者概率DP,设dp[i]表示已经投出i次1平均还要投多少次才能到达目标状态。
则有dp[i] = 1/6*(1+dp[i+1]) + 5/6*(1+dp[i]).
边界条件dp[m]=0。
然后就得到m1>=(6^n-1)/30,m2>=(6^n-1)/5.显然最小的m2 = (6^n-1)/5
而(6^n-1)%30 !=0,通过观察发现有6%30=6,6*6%30=6,那么就有6^n%30=6也就是说虽然6^n%30 !=0,但是6^n+24%30 == 0,且这就是m1,
即有m1 = (6^n+24)/30.....现在就是求m1%2011,m2%2011,我是用的先算6^n%2011,用快速幂,然后求出分别求出30和5对应2011的逆元。
至于求逆元,用扩展欧几里得算法即可·······甚至用电脑暴力算出也行
贴代码:
1 #include<cstdio> 2 const int mod = 2011; 3 const int e1 = 1944,e2 = 1609;//30,5的逆元 4 inline int qPow(int x,int p) 5 { 6 int a=6,ans=1; 7 while(x) 8 { 9 if(x&1) ans = ans*a%p; 10 a = a*a%p; 11 x >>= 1; 12 } 13 return ans; 14 } 15 int main() 16 { 17 // freopen("in.txt","r",stdin); 18 int n,tmp,tmp1,tmp2,ans1,ans2; 19 while(scanf("%d",&n),n) 20 { 21 tmp = qPow(n,mod); 22 tmp1 = (tmp+24+mod)%mod; 23 tmp2 = (tmp-1+mod)%mod; 24 ans1 = tmp1*e1%mod; 25 ans2 = tmp2*e2%mod; 26 printf("%d %d\n",ans1,ans2); 27 } 28 return 0; 29 }