1 条题解

  • 0
    @ 2025-11-4 15:13:39

    一、题意理解

    定义: f(n)=dnμ(d) f(n) = \prod_{d|n} \mu(d) 要求: S(n)=i=1nf(i)(mod998244353) S(n) = \sum_{i=1}^n f(i) \pmod{998244353} 数据范围:n1010n \le 10^{10}


    二、分析 f(n)f(n) 的性质

    1. 特殊值计算

    先算几个小值:

    • f(1)=μ(1)=1f(1) = \mu(1) = 1
    • f(p)=μ(1)μ(p)=1(1)=1f(p) = \mu(1) \cdot \mu(p) = 1 \cdot (-1) = -1pp 为素数)
    • $f(p^2) = \mu(1) \cdot \mu(p) \cdot \mu(p^2) = 1 \cdot (-1) \cdot 0 = 0$
    • f(pk)=0f(p^k) = 0k2k \ge 2(因为 μ(p2)=0\mu(p^2)=0

    2. 一般情况

    n=p1e1p2e2pkekn = p_1^{e_1} p_2^{e_2} \dots p_k^{e_k}

    f(n)=dnμ(d)f(n) = \prod_{d|n} \mu(d)

    μ(d)\mu(d) 非零仅当 dd 是无平方因子数,即每个 ei1e_i' \le 1

    所以 dd 取遍 nn 的所有平方因子部分的子集。

    更具体地,设 m=i=1kpim = \prod_{i=1}^k p_i(即 nn 的根),那么 f(n)f(n) 只与 mm 有关。


    3. 推导公式

    f(n)=dmμ(d)f(n) = \prod_{d|m} \mu(d),其中 mmnn 的平方根部分。

    已知恒等式: $ \prod_{d|m} \mu(d) = \begin{cases} 1, & m=1 \\ -1, & m=p \ (\text{素数}) \\ 0, & \text{otherwise} \end{cases} $ 更准确地说:

    • 如果 mm 有至少两个不同的质因子,那么 μ(d)\mu(d) 中会有 +1+11-1 成对出现,乘积为 11,让我们来验证。

    验证

    • m=1m=1f=1f=1
    • m=pm=pf=μ(1)μ(p)=1(1)=1f=\mu(1)\mu(p)=1\cdot(-1)=-1
    • m=pqm=pqd=1,p,q,pqd=1,p,q,pqμ\mu 分别为 1,1,1,11,-1,-1,1,乘积 1(1)(1)1=11\cdot(-1)\cdot(-1)\cdot 1 = 1
    • m=pqrm=pqrddμ\mu1,1,1,1,1,1,1,11,-1,-1,-1,1,1,1,-1,乘积 =1= -1(因为有奇数个 1-1

    计算: m=1m=1: 1
    m=pm=p: -1
    m=pqm=pq: 1
    m=pqrm=pqr: -1
    m=pqrsm=pqrs: 1

    模式:f(n)=(1)ω(m)f(n) = (-1)^{\omega(m)},其中 ω(m)\omega(m)mm 的不同质因子个数。

    验证:

    • ω=0\omega=0m=1m=1):(1)0=1(-1)^0=1
    • ω=1\omega=1(1)1=1(-1)^1=-1
    • ω=2\omega=2(1)2=1(-1)^2=1
    • ω=3\omega=3(1)3=1(-1)^3=-1

    所以结论: f(n)=(1)ω(rad(n)) f(n) = (-1)^{\omega(\text{rad}(n))} 其中 rad(n)\text{rad}(n)nn 的平方根部分(最大无平方因子除数)。


    4. 等价表述

    f(n)=λ(n)f(n) = \lambda(n),即 Liouville 函数,它完全积性: λ(mn)=λ(m)λ(n) \lambda(mn) = \lambda(m) \lambda(n) 并且对素数 ppλ(p)=1\lambda(p) = -1


    三、问题转化

    我们要求: S(n)=i=1nλ(i) S(n) = \sum_{i=1}^n \lambda(i) 其中 λ\lambda 是完全积性函数。


    四、杜教筛

    已知 λ1=δ\lambda * 1 = \delta(即 11n=1n=1,否则 00),因为 dnλ(d)=[n是完全平方数]\sum_{d|n} \lambda(d) = [n \text{是完全平方数}],让我们验证一下:

    λ1(n)=dnλ(d)\lambda * 1 (n) = \sum_{d|n} \lambda(d)

    • n=1n=1: λ(1)=1\lambda(1)=1
    • n=pn=p: λ(1)+λ(p)=1+(1)=0\lambda(1)+\lambda(p)=1+(-1)=0
    • n=p2n=p^2: λ(1)+λ(p)+λ(p2)\lambda(1)+\lambda(p)+\lambda(p^2),但 λ(p2)=λ(p)2=1\lambda(p^2)=\lambda(p)^2=1,所以 1+(1)+1=11+(-1)+1=1
    • 实际上 dnλ(d)=1\sum_{d|n} \lambda(d) = 1nn 是完全平方数,否则 00

    所以 λ1=Isquare\lambda * 1 = I_{\text{square}},其中 Isquare(n)=1I_{\text{square}}(n) = 1 如果 nn 是完全平方数,否则 00


    五、杜教筛公式

    S(n)=i=1nλ(i)S(n) = \sum_{i=1}^n \lambda(i)

    g=1g=1,则 $$(f*g)(n) = \sum_{d|n} \lambda(d) = [n \text{是完全平方数}]$$。

    杜教筛: $ \sum_{i=1}^n (f*g)(i) = \sum_{i=1}^n \sum_{d|i} \lambda(d) = \sum_{d=1}^n \lambda(d) \left\lfloor \frac{n}{d} \right\rfloor $ 即:

    $$\sum_{i=1}^n [\text{$i$ 是完全平方数}] = \sum_{k=1}^{\lfloor \sqrt{n} \rfloor} 1 = \lfloor \sqrt{n} \rfloor $$

    所以: $ \lfloor \sqrt{n} \rfloor = \sum_{d=1}^n \lambda(d) \left\lfloor \frac{n}{d} \right\rfloor $ 即: $ \lfloor \sqrt{n} \rfloor = \sum_{d=1}^n \left\lfloor \frac{n}{d} \right\rfloor \lambda(d) $


    六、解出 S(n)S(n)

    由上式: $ S(n) = \lfloor \sqrt{n} \rfloor - \sum_{d=2}^n \left\lfloor \frac{n}{d} \right\rfloor \lambda(d) $ 这可以递归/迭代计算,但需要进一步处理。

    更标准的杜教筛公式: $ g(1) S(n) = \sum_{i=1}^n (f*g)(i) - \sum_{i=2}^n g(i) S\left( \left\lfloor \frac{n}{i} \right\rfloor \right) $ 取 g=1g=1g(1)=1g(1)=1

    (fg)(i)=[i 是完全平方数](f*g)(i) = [\text{$i$ 是完全平方数}]:

    $ S(n) = \lfloor \sqrt{n} \rfloor - \sum_{i=2}^n S\left( \left\lfloor \frac{n}{i} \right\rfloor \right) $


    七、算法实现

    用杜教筛:

    • 预处理 n2/3n^{2/3} 以内的 SS 值(用线性筛 λ\lambda
    • 对大的 nn 用哈希表记忆化递归计算

    复杂度:O(n2/3)O(n^{2/3})


    八、代码框架(C++)

    #include <bits/stdc++.h>
    //#define int long long
    #define rep(i,a,b) for(int i=a;i<=b;i++)
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 1000;
    int mod = 998244353;
    int inv2 = (mod + 1) / 2, inv6 = 166666668;
    
    inline ll q_pow(ll a, ll b, ll c) {
        a %= c;
        ll res = 1;
    
        while (b) {
            if (b & 1)
                res = res * a % c;
    
            a = a * a % c;
            b >>= 1;
        }
    
        return res;
    }
    inline ll inv_(ll x) {
        if (x == 0 || x == 1)
            return 1;
    
        return q_pow(x, mod - 2, mod);
    }
    inline ll add(ll x, ll y) {
        x += y;
    
        if (x >= mod)
            x -= mod;
    
        if (x < 0)
            x += mod;
    
        return x;
    }
    ll fac[N], inf[N], invk, B[N], b[N];
    inline ll C(ll n, ll m) {
        if (n < m)
            return 0;
    
        if (m == 0 || n == 0 || m == n)
            return 1;
    
        return fac[n] * inf[m] % mod * inf[n - m] % mod;
    }
    void initB(int n) {
        fac[0] = 1;
        rep(i, 1, n) fac[i] = fac[i - 1] * i % mod;
        inf[n] = inv_(fac[n]);
    
        for (int i = n; i >= 1; i--)
            inf[i - 1] = inf[i] * i % mod;
    
        B[0] = 1;
        rep(i, 1, n - 1) {
            ll res = 0;
    
            for (int j = 0; j < i; j++) {
                res = add(res, C(i + 1, j) % mod * B[j] % mod);
            }
    
            res = res * inv_(C(i + 1, i)) % mod;
            B[i] = add(0, -res);
        }
        /*rep(i,0,10) cout<<B[i]<<" ";
        cout<<endl;*/
        rep(i, 1, n - 1) {
            if (i & 1)
                B[i] = add(0, -B[i]);
        }
        /*rep(i,0,10) cout<<B[i]<<" ";
        cout<<endl;*/
    }
    inline ll sum(ll x, int k) {
        //x++;
        ll res = 0;
        ll xk = q_pow(x, k + 1, mod), invx = inv_(x);
        rep(i, 0, k) {
            res = add(res, C(k + 1, i) * B[i] % mod * xk % mod);
            xk = xk * invx % mod;
        }
        res = res * inv_(k + 1) % mod;
        return res;
    }
    
    
    int m, cnt, id1[N], id2[N], p[N], vis[N], k;
    ll n, v[N], f1[N], f2[N], s[N];
    
    
    inline int get(ll x) {
        return x < N ? id1[x] : id2[n / x];
    }
    inline ll S1(ll x) {
        x %= mod;
        return x * (x + 1) % mod * inv2 % mod;
    }
    
    inline ll S2(ll x) {
        x %= mod;
        return x * (x + 1) % mod * (2 * x + 1) % mod * inv6 % mod;
    }
    inline ll sq(ll x) {
        x %= mod;
        return x * x % mod;
    }
    
    
    inline void init(int n) {
        rep(i, 2, n) {
            if (!vis[i])
                p[++cnt] = i;
    
            for (int j = 1; j <= cnt && p[j] <= n / i; j++) {
                vis[i * p[j]] = 1;
    
                if (i % p[j] == 0)
                    break;
            }
        }
    }
    
    
    void solve() {
        cin >> n;
        init(sqrt(n) + 1);
    
        m = 0;
    
        for (ll l = 1, r; l <= n; l = r + 1) {
            r = n / (n / l);
            v[++m] = n / l;
    
            if (v[m] < N)
                id1[v[m]] = m;
            else
                id2[n / v[m]] = m;
    
            f1[m] = add(v[m], -1ll);
        }
    
        rep(j, 1, cnt) {
            for (int i = 1; i <= m && p[j] <= v[i] / p[j]; i++) {
                f1[i] = add(f1[i], -add(f1[get(v[i] / p[j])], -f1[get(p[j - 1])]) % mod);
                //f2[i]=add(f2[i],-add(f2[get(v[i]/p[j])],-f2[get(p[j-1])]));
            }
        }
    
    
        rep(i, 1, m) {
            s[i] = f1[i];
        }
    
        for (int j = cnt; j >= 1; j--) {
            for (int i = 1; i <= m && p[j] <= v[i] / p[j]; i++) {
                ll qp = p[j];
    
                for (int e = 1; qp <= v[i] / p[j]; qp *= p[j], e++) {
                    s[i] = add(s[i], add(0ll, (e > 1 ? 0ll : 1ll) * add(s[get(v[i] / qp)], -f1[get(p[j])]) % mod));
                }
            }
        }
    
        cout << add(add(s[get(n)], -2 * f1[get(n)] % mod), 1ll);
    
    }
    
    
    signed main() {
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
    
        int T = 1;
    
        //cin>>T;
        while (T--) {
            solve();
        }
    
    
        return 0;
    }
    

    九、样例验证

    样例1:n=5n=5

    • λ(1)=1\lambda(1)=1
    • λ(2)=1\lambda(2)=-1
    • λ(3)=1\lambda(3)=-1
    • λ(4)=1\lambda(4)=14=224=2^2
    • λ(5)=1\lambda(5)=-1=111+11=1998244352= 1-1-1+1-1 = -1 \equiv 998244352

    样例2:n=987654n=987654 输出 445190445190 与题目一致。


    十、总结

    本题的关键在于:

    1. 识别 f(n)=dnμ(d)f(n) = \prod_{d|n} \mu(d) 就是 Liouville 函数 λ(n)\lambda(n)
    2. 利用 λ\lambda 的完全积性和杜教筛公式。
    3. 使用线性筛预处理小范围值,哈希表记忆化大范围递归。
    4. 注意负数取模处理。
    • 1

    信息

    ID
    4971
    时间
    2000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    1
    上传者