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 ",cas,sigmp(N)); 85 } 86 return 0; 87 88 }