CF 1097D Makoto and a Blackboard

算是记一下昨天晚上都想了些什么

官方题解   点我

简单题意

给定两个正整数$n$和$k$,定义一步操作为把当前的数字$n$等概率地变成$n$的任何一个约数,求$k$步操作后的期望数字,模$1e9 + 7$。

$$n \leq 10^{15}, k \leq 10^4$$

我的思路

设$f(n, k)$表示$n$在$k$步操作之后的期望数字,假设$n$的约数有$m$个,分别为$d_1, d_2, \dots, d_m$,有递推式

$$f(n, k) = \frac{1}{m}\sum_{i = 1}^{m}f(d_i, k - 1)$$

边界条件显然是$f(n, 0) = n$。

直接暴算的话一共有$n * k$个状态,无法承受。

接下来证明:$f(a, k) * f(b, k) = f(ab, k)$,(其中$a, b$互质)。

数学归纳法来了(逃)

1、$k = 0$的时候显然成立。

2、假设在$k - 1$的时候成立。

我们设$a$有$n$个约数,$b$有$m$个约数,因为约数个数$\sigma$是一个积性函数,所以$ab$的约数个数有$nm$个。

那么根据递推式,有

$$f(ab, k) = \frac{1}{nm}\sum_{d | ab}f(d, k - 1)$$

$$f(a, k) * f(b, k) = \frac{1}{n}\sum_{i | a}f(i, k - 1)\frac{1}{m}\sum_{j | b}f(j, k - 1) = \frac{1}{nm}\sum_{i | a}\sum_{j | b}f(i, k - 1) * f(j, k - 1)$$

要证$f(ab, k) = f(a, k) * f(b, k)$,

即证$\sum_{d | ab}f(d, k - 1) = \sum_{i | a}\sum_{j | b}f(i, k - 1) * f(j, k - 1)$,

右边的式子变形一下

$$\sum_{i | ab}\sum_{j | i}f(i, k - 1) * f(\frac{i}{j}, k - 1)[gcd(j, \frac{i}{j} == 1)]$$

把$ab$分解质因数变成$\prod_{i = 1}^{m}p_i^{c_i}$的形式。

注意到$a$、$b$互质,那么$gcd(j, \frac{i}{j}) == 1$的时候其实只有一种,那就是$j$恰好取完了某个或某几个$p_i^{c_i}$的时候,这时候有$f(d, k - 1) = f(i, k - 1) * f(\frac{d}{i}, k - 1)(gcd(d, a) == i)$。

代进去之后发现两式相等了。

这样子的话我们就得到了$f$函数一个类似于积性的性质,于是我们可以直接把$n$分解成$\prod_{i = 1}^{m}p_i^{c_i}$的形式,然后分别计算每一个$p_i^{c_i}$的答案最后乘起来。

发现这样子状态数十分有限,只有$klogn$个,所以直接暴力算就可以了。

时间复杂度应当是$O(\sqrt{n} + klogn)$。

代码非常乱。

Code:

#include <cstdio>
#include <cstring>
#include <map>
#include <vector>
#include <algorithm>
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
typedef pair <ll, int> pin;

const ll P = 1e9 + 7;

ll ans = 1LL, g[80][10005], inv[80];
bool vis[80][10005];

map <pin, ll> mp;

template <typename T>
inline void inc(T &x, T y) {
    x += y;
    if (x >= P) x -= P; 
}

inline ll fpow(ll x, ll y) {
    ll res = 1LL;
    for (; y > 0; y >>= 1) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

ll f(ll p, int m, int k) {
    if (p == 1) return 1LL;
    if (k == 0) return fpow(p, m);
    if (vis[m][k]) return g[m][k];
    
    vis[m][k] = 1;
    
    ll res = 0;
    rep(i, 0, m) inc(res, f(p, i, k - 1) * inv[m + 1] % P);
    
    return g[m][k] = res;
}

inline void solve(ll p, int m, int k) {
    rep(i, 0, m) rep(j, 0, k) g[i][j] = 0, vis[i][j] = 0; 
    ans = ans * f(p, m, k) % P;
}

int main() {
//    freopen("Sample.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);

    rep(i, 0, 79) inv[i] = fpow(i, P - 2);
    
    ll n; int k;
    scanf("%I64d%d", &n, &k);
    
    ll tmp = n;
    for (ll i = 2; i * i <= n; i++) {
        if (tmp % i == 0) {
            int m = 0;
            for (; tmp % i == 0; tmp /= i, ++m);
            solve(i, m, k);
        }
    }    
    if (tmp > 1) solve(tmp, 1, k);
    
    printf("%I64d\n", ans);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/CzxingcHen/p/10223491.html