题目
题意:
给定一个n,每次操作n都等概率的变为它的因子,输出k次操作后n的值的数学期望。
分析:
由于是因子,所以我们自然就往素因子分解方向思考。由于n的数字很大,我们求它的因子数就是一个很麻烦的事情,更何况又要求因子,状态数太多了。其实我们可以把它拆成素因子,每个素因子的操作彼此是不影响的,所以对于n的操作,可以变成对它的素因子进行操作,最后把素因子的期望相乘即可。
dp[i][j]表示在第i次操作后当前素因子的阶数为j的概率,dp[i][j]就可以从dp[i-1][t] (t>=j&&t<=最大值),最后概率乘上值即可。比较绕需要认真分析一下。
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
ll mod = 1e9 + 7;
vector<ll> num,count;
ll dp[10005][60];
ll inv[65];
ll q_pow(ll a,ll b)
{
ll res = 1;
while( b )
{
if( b & 1 ) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
void divide(ll n)
{
ll a = n;
for (ll i = 2; i * i <= n; i++)
{
if( a % i == 0 )
{
num.push_back(i);
int t = 0;
while( a % i == 0 )
{
a /= i;
t ++;
}
count.push_back(t);
}
}
if( a > 1 )
{
num.push_back(a);
count.push_back(1);
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
inv[1] = 1;
for (int i = 2; i <= 60; i++)
{
inv[i] = mod - (mod/i) * inv[mod%i] % mod;
}
ll n,k;
cin >> n >> k;
divide(n);
ll ans = 1;
for (int i = 0; i < num.size(); i++)
{
ll temp = 0;
for (int j = 0; j <= k; j++)
{
for (int l = 0; l <= count[i]; l++)
{
if( j == 0 )
{
if( l == count[i] ) dp[j][l] = 1;
else dp[j][l] = 0;
}
else
{
dp[j][l] = 0;
for (int t = l; t <= count[i]; t++)
{
dp[j][l] += (dp[j-1][t] * inv[t+1]) % mod;
dp[j][l] %= mod;
}
}
if( j == k ) temp = (temp + (dp[j][l] * q_pow(num[i],l)) % mod ) % mod;
}
}
ans *= temp;
ans %= mod;
}
cout << ans << '\n';
return 0;
}