1 /*UVA11762*/
2 /*概率期望:
3 题目:给出一个整数n,每次可以在不超过n的素数中随机选择一个p,如果p是n的约数,则n变成n/p,否则不变。
4 问平均情况要多少次随机选择,才能把n变成1?
5 解题步骤:举例、归纳方法
6 举例:
7 例N=13 ,可得素数2,3,5,7,11,13, 发现只有选到13时才能变成1,ans=1/p,p=1/6,ans=6;
8 例N=15, 可得素数2,3,5,7,11,13,发现只能选到3,5;
9 若选到3,N=5,素数2,3,5, 必须要选到5
10 若选到5,N=3,素数2,3, 必须选到3
11 p=(1/6)*(1/3)+(1/6)*(1/2) ans=1/p=36/5
12 归纳:
13 关键是求p(由上可看出是运用乘法,加法原理)
14 递归方法:f(N)=sigm求和(1/Q[N]*f(N/i),2<=i<=N且i是素数且N%i==0),N>1;
15 1,N=1;
16 注意f(N)可以记忆化
17 Q[N]=不超过n的素数的个数,例Q[15]=6,可预先求出
18 但是可惜,上面的方法错了= =,换句话说,p是对的,而1/P不是答案
19 改之:马尔可夫过程
20 f(x)=1+f(x)*(Q(x)-g(x))/p(x)+sigm(f(x/y)/Q(x),y是素数因子,g(x)是y的总个数
21 移项后化简:f(x)=(sigm(f(x/y))+Q(x))/g(x)
22 */
23 #include<iostream>
24 #include<stdio.h>
25 #include<string.h>
26 #include<algorithm>
27 #include<stdlib.h>
28 #include<math.h>
29 #include<queue>
30 #include<vector>
31 #include<map>
32
33 using namespace std;
34
35 const int maxn = 1000000;//素数打表
36 bool flag[maxn+5];
37 int prim[maxn/3], cnt;
38 void calc_prim(){
39 cnt=0;
40 for(int i = 2; i <= maxn; i ++){
41 if(!flag[i]) prim[cnt++] = i;
42 for(int j = 0; j < cnt && prim[j]*i <= maxn; j ++){
43 flag[i*prim[j]] = 1;
44 if(i%prim[j]==0) break;
45 }
46 }
47 return ;
48 }//最终有cnt个素数prim[0]--prim[cnt-1]
49 int Q[maxn+5];
50 void builtQ()
51 {
52 calc_prim();
53 Q[2]=1,Q[3]=2;
54 for(int i=4;i<=maxn;i++)
55 if (!flag[i]) Q[i]=Q[i-1]+1;else Q[i]=Q[i-1];
56 //利用了素数非连续性的特点
57 /*Q[x]=Q[x-1]+1,if x is prime,else Q[x]=Q[x-1]*/
58 }
59 double F[maxn+5];//记忆化搜索用
60 double sigmp(int N)
61 {
62 if (F[N]!=-1.0) return F[N];
63 double sum=0;int tot=0;
64 for(int i=2;i<=N;i++)
65 {
66 if (!flag[i] && N%i==0) sum+=sigmp(N/i),tot++;
67 }
68 sum=(sum+Q[N])/tot;//累加上无法约简的部分
69 return F[N]=sum;
70 }
71 int main()
72 {
73 int t,N;
74 builtQ();
75 /*memset(F,-1,sizeof(F));*/
76 /*double 可以初始化为0,但不可以为-1*/
77 for(int i=2;i<=maxn;i++) F[i]=-1.0;
78 F[1]=0.0;
79 cin>>t;
80
81 for(int cas=1;cas<=t;cas++)
82 {
83 cin>>N;
84 printf("Case %d: %.7lf\n",cas,sigmp(N));
85 }
86 return 0;
87
88 }