题解P3768 简单的数学题

P3768 简单的数学题

非常典型的一道杜教筛求前缀和题目

首先我们先顺着题目给出的式子往下面推

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^nijgcd(i,j)=\sum\limits_{d=1}^n\sum\limits_{i=1}^n\sum\limits_{j=1}^nijd[gcd(i,j)=d]\\=\sum\limits_{d=1}^nd^3\sum\limits_{i=1}^{\frac{n}{d}}\sum\limits_{j=1}^{\frac{n}{d}}ij[gcd(i,j)=1] \]

用莫比乌斯反演,令 \(g(n)=\sum\limits_{i=1}^n\sum\limits_{j=1}^nij\)

\[\Rightarrow ans=\sum\limits_{d=1}^nd^3\sum\limits_{i=1}^{\frac{n}{d}}\mu(i)i^2g(\frac{n}{id}) \]

\(T=id\),尝试交换循环顺序

\[ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\sum\limits_{d|T}d\mu(\frac{T}{d}) \]

\(\sum\limits_{d|T}d\mu(\frac{T}{d})\) 写成迪利克雷卷积的形式 \(\mu*id=\phi\)

\[\Rightarrow ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\phi(T) \]

\(f(n)=n^2\phi(n)\)

\[ans=\sum\limits_{T=1}^ng(\frac{n}{T})f(T) \]

对于 \(g\) 可以使用数论分块,考虑如何快速求 \(S(n)=\sum\limits_{i=1}^nf(i)\)

首先搬出杜教筛式子 \(g(1)S(n)=\sum\limits_{i=1}^n(f*g)(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)\)

\[(f*g)(n)=\sum\limits_{d|n}g(d)f(\frac{n}{d})=\sum\limits_{d|n}\frac{n^2}{d^2}\phi(\frac{n}{d})g(d) \]

不妨令 \(g(i)=i^2\)

\[(f*g)(n)=\sum\limits_{d|n}n^2\phi(\frac{n}{d})=n^2\sum\limits_{d|n}\phi(\frac{n}{d})=n^3 \]

\[\Rightarrow S(n)=\sum\limits_{i=1}^ni^3-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor) \]

\[ans=\sum\limits_{T=1}^ng(\frac{n}{T})T^2\phi(T) \]

前面那坨可以数论分块,后面那一坨可以杜教筛 \(O(n^{\frac{2}{3}})\) 处理前缀和,注意取模

#include<iostream>
#include<cstdio>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int N = 8000001;

bool st[N];
int prime[N], tot;
ll sum[N], p, n, phi[N], inv2, inv6;
unordered_map<ll, ll> S;

void init()
{
    phi[1] = 1;
    for (int i = 2; i < N; i++)
    {
        if (!st[i])
            prime[++tot] = i, phi[i] = i - 1;
        for (int j = 1; i * prime[j] < N; j++)
        {
            st[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                phi[i * prime[j]] = (phi[i] * prime[j]) % p;
                break;
            }
            phi[i * prime[j]] = (phi[i] * (prime[j] - 1)) % p;
        }
    }
    for (int i = 1; i < N; i++)
        sum[i] = (sum[i - 1] + (ll)phi[i] * i % p * i % p) % p;
}

ll Sum1(ll x)
{
    ll res = (((x + 1) % p * x % p) % p) * inv2 % p;
    return res * res % p;
}

ll Sum2(ll x)
{
    ll r1 = (x % p * (x + 1) % p) % p;
    return ((r1 * ((x + x + 1) % p)) % p * inv6) % p;
}

ll Djf(ll x)
{
    if (x < N)
        return sum[x];
    if (S[x])
        return S[x];
    ll res = Sum1(x);
    for (ll l = 2, r; l <= x; l = r + 1)
    {
        r = min(x, (x / (x / l)));
        res -= (Sum2(r) - Sum2(l - 1))* (Djf(x / l)) % p;
        res %= p;
    }
    return S[x] = (res + p) % p;
}

ll qmi(ll a, ll b)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

int main()
{
    scanf("%lld%lld", &p, &n);
    init();
    inv2 = qmi(2, p - 2), inv6 = qmi(6, p - 2);
    ll ans = 0;
    for (ll l = 1, r; l <= n; l = r + 1)
    {
        r = min(n, n / (n / l));
        ans = (ans + (Sum1(n / l) % p * ((Djf(r) - Djf(l - 1)) % p)) % p + p) % p;
    }
    printf("%lld", ans);
    return 0;
}
posted @ 2021-04-21 17:17  DSHUAIB  阅读(48)  评论(0编辑  收藏  举报