1. 程式人生 > >CF 1097D Makoto and a Blackboard

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