给一个数 n ,每次操作这个 n 会等概率的变成它的一个因子,问经过多少次操作,这个数 n 变成 1,求期望操作次数
思路
一道很明显,有经典的期望 dp,
期望 dp 是从后往前递推而来,对于这题来说就是:从最终状态 1 -> 递推出 n 这个值期望的次数,
总感觉这样说不太对,其实我的理解就是,我们对当前的数 x 进行一次操作后 x 会变成的它的因子,那么当前的 dp 状态就要从它的子状态(即:因子的 dp 状态)转移而来。
我们设从 x 变到 1 期望次数为 dp [x],
举个转移的例子:对于 x = 6 的时候,6 的因子有 6、3、2、1 共 4 个,那状态转方程为:
因此我们可以预处出 1~1e5 中每个数的因子,然后从 后往前进行 dp 状态转移
代码
#include<bits/stdc++.h>
using namespace std;#definedbdouble#definelllonglong#definescscanf#defineprprintf#definefifirst#definesesecond#definepbpush_back#definem_pmake_pair#definePirpair<int,int>#defineinf0x3f3f3f3f#defineINF0x3f3f3f3f3f3f3f3f/*==========ACMer===========*/constint N =1e5+10;
vector<int> fac[N];voidinit(){for(int i =1; i < N; i ++){for(int j =1; j * j <= i; j ++){if(i % j ==0){
fac[i].pb(j);if(i / j != j) fac[i].pb(i / j);}}}}
db dp[N];voidpre_do(){init();for(int i =2; i < N; i ++){int m = fac[i].size();
db p =1.0/(m -1);
dp[i]= m * p;for(auto x : fac[i]){if(x == i)continue;
dp[i]+= dp[x]* p;}}}intmain(){pre_do();int T, cas =1;sc("%d",&T);while(T --){int n;sc("%d",&n);pr("Case %d: %f\n", cas ++, dp[n]);}return0;}