757E. Bash Plays with Functions 题解

757E. Bash Plays with Functions

题意:定义函数

\[f_0(n) = \sum_{u * v =n} [gcd(u, v) = 1] \]

\[f_{r + 1}(n) = \sum_{u*v = n} \frac{f_r(u) + f_r(v)}{2} \]

给定 \(q\) 次询问,每次询问给出 \(r,n\),输出 \(f_r(n)\) 答案对 \(1e9 + 7\) 取模

解法:积性函数

首先把柿子化成容易计算的形式,从 \(f_0(n)\) 开始

\(n\) 分解成 \(n = p_1^{\alpha_1} * p_2^{\alpha_2} * ...*p_k^{\alpha_k}\)

那么根据定义,\(u = p_1^{\beta_1} * p_2^{\beta_2} * ... *p_k^{\beta_k}\)\(v = \frac{n}{u}\)

由于 \(gcd(u, v) = 1\),所以 \(\forall i \in [1, k],\beta_i = 0 \, or \,\alpha_i\)

\(f_0(n) = 2^k\),注意到其与具体的值无关,只与次数有关。

容易看出 \(f_0(n)\) 是一个积性函数

\[f_{r + 1}(n) = \sum_{u*v = n} \frac{f_r(u) + f_r(v)}{2} = \sum_{d|n} \frac{f_r(d) + f_r(\frac{n}{d})}{2} \]

可以看出,\(\forall d | n\)\(f_r(d)\) 都枚举了两次,故可以进一步化简为

\[f_{r + 1}(n) = \sum_{d|n} \frac{2 * f_r(d)}{2} = \sum_{d|n} f_r(d) \]

\(f_r(n)\) 为积性函数,接下来证明 \(f_{r + 1}(n)\) 也是积性函数

\[f_{r + 1}(p * q) = \sum_{d|p*q} f_r(d) \]

\[= \sum_{d_1 * d_2|p * q} f_r(d_1 * d_2) \]

\[= \sum_{d_1 * d_2 | p * q} f_r(d_1) * f_r(d_2) \,(f_r(n) 为积性函数) \]

\[= \sum_{d_1 | p}f_r(d_1) * \sum_{d_2 | q} f_r(d_2) = f_{r + 1}(p) * f_{r + 1}(q) \]

(抱歉不知道如何对齐)

根据数学归纳法,\(f_r(n)\) 是积性函数

那么对于任何一次询问,设 \(n = p_1^{\alpha_1} * p_2^{\alpha_2} * ...*p_k^{\alpha_k}\),我们只需要算出 \(f_r(p_i^{\alpha_i})\),然后根据积性函数的性质乘起来即可。

\[f_0(p^{\alpha}) = 1 \]

\[f_1(p^{\alpha}) = 1 + 2*\alpha \]

\[f_r(p^\alpha) = \sum_{i = 0}^\alpha f_{r - 1}(p^i) \]

接下来的递推求解即可,注意到函数的值与质数大小无关,只与次数有关,故该函数可以在 \(n log^2 n\) 的时间内完成

接下来只要对每次询问,分解出 \(n\) 即可,注意到 \(n\) 只有 \(1e6\),可以预处理下每个数的最小质因子,然后递推回 \(1\) 即可,可以在 \(logn\) 的时间内完成,总复杂度为 \(O(nlog^2n + n + qlogn)\)

#include <bits/stdc++.h>
#define endl '\n'
#define ls u << 1
#define rs u << 1 | 1
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL,LL> PLL;
const int INF = 0x3f3f3f3f, N = 1e6 + 10;
const int MOD = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
inline int lowbit(int x) {return x & (-x);}

LL f[N][22];
bool st[N];
int last[N], pr[N], num;

void init() {
    int n = 1000000;
    for (int i = 0; i <= n; i ++ ) f[i][0] = 1;
    for (int j = 1; j <= 20; j ++ ) {
        for (int i = 0; i <= n; i ++ ) {
            if (i == 0) f[i][j] = 2;
            else if (i == 1) f[i][j] = 1 + 2 * j;
            else for (int k = 0; k <= j; k ++ ) f[i][j] = (f[i][j] + f[i - 1][k]) % MOD;
        }
    }
    for (int i = 2; i <= n; i ++ ) {
        if (!st[i]) pr[++ num] = i, last[i] = i;
        for (int j = 1; 1ll * pr[j] * i <= n; j ++ ) {
            st[i * pr[j]] = true;
            last[i * pr[j]] = pr[j];
            if (i % pr[j] == 0) break;
        }
    }
}

inline void solve() {
    int r, n; cin >> r >> n;
    LL res = 1;
    while (n != 1) {
        int cnt = 0, p = last[n];
        while (n % p == 0) n /= p, cnt ++;
        res = res * f[r][cnt] % MOD;
    }
    cout << res << endl;
}
int main() {
#ifdef DEBUG
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    auto now = clock();
#endif
    ios::sync_with_stdio(false), cin.tie(nullptr);
    cout << fixed << setprecision(2);
    init();
    int T; cin >> T;
    while (T -- )
        solve();
#ifdef DEBUG
    cout << "============================" << endl;
    cout << "Program run for " << (clock() - now) / (double)CLOCKS_PER_SEC * 1000 << " ms." << endl;
#endif
    return 0;
}
posted @ 2021-11-01 23:58  Time_Limit_Exceeded  阅读(49)  评论(0)    收藏  举报