P3768 简单的数学题(杜教筛+欧拉函数反演或者莫比乌斯反演)

https://www.luogu.org/problem/P3768

题意很简单就是求这个:

ans=\sum_{i=1}^{n}\sum_{j=1}^{n}ijgcd(i,j)

为了方便我们用(i,j)表示gcd(i,j),然后开始快乐的推公式吧:

ans=\sum_{d=1}^{n}d\sum_{i=1}^{n}\sum_{j=1}^{n}ij[(i,j)=d]=\sum_{d=1}^{n}d^{3}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}ij[(i,j)=1]

看见后面那一坨直接莫比乌斯反演:

ans=\sum_{d=1}^{n}d^{3}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}ij \sum_{t|(i,j)}\mu(t)

然后根据套路枚举t:

ans=\sum_{d=1}^{n} d^{3} \sum_{t=1}^{\left \lfloor \frac{n}{d} \right \rfloor}t^{2}\mu(t) \sum_{t=1}^{\left \lfloor \frac{n}{td} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{td} \right \rfloor}ij

后面一部分就是等差数列通项公式,所以后面就是变形为sum(\frac{n}{td})^{2}

然后再根据套路令T=td,枚举T:

ans=\sum_{T=1}^{n}sum(\frac{n}{T})^{2}\sum_{d|T}d^{2}(\frac{T}{d})^{3}\mu(d)=\sum_{T=1}^{n}sum(\frac{n}{T})^{2}T^{2}\sum_{d|T}d\mu(\frac{T}{d})

我们观察\sum_{d|T}d\mu(\frac{T}{d})这部分刚好是狄利克雷卷积的标准形式id*\mu=\varphi

因此这部分就是欧拉函数。所以最终公式:

ans=\sum_{T=1}^{n}sum(\frac{n}{T})^{2}T^{2}\varphi(T)

其实这都是吃饱了的做法,如果不直接无脑用莫比乌斯反演而是用这个n=\sum_{d|n}\varphi(d),在题目上的那个式子中吧gcd给替换了看看呢:

ans=\sum_{i=1}^{n}\sum_{j=1}^{n}ij\sum_{d|(i,j)}\varphi(d)

然后枚举d:

ans=\sum_{d=1}^{n}\varphi(d)d^{2}\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\sum_{j=1}^{\left \lfloor \frac{n}{d} \right \rfloor}ij=\sum_{d=1}^{d}\varphi(d)d^{2}sum(\frac{n}{d})^{2}

是不是这样要简单很多呢,我们暂且称为欧拉反演吧。

观察上面的式子前面可以分块做,那就要求d^{2}\varphi(d)的前缀和了,那当然就是杜教筛了。

我们卷积平方就可以了:(f*g)(n)=\sum_{d|n}d^{2}\varphi(d)(\frac{n}{d})^{2}=n^{3}

所以杜教筛就可以这样筛了:S(n)=\sum_{i=1}^{n}n^{3}-\sum_{d=2}^{n}d^{2}S(\frac{n}{d})

其他就没什么了。

#include "bits/stdc++.h"

using namespace std;
const int N = 5000000 + 10;
typedef long long ll;
int vis[N], cnt = 0;
ll pri[N], phi[N], p, sum_f[N], inv6, inv2, n;
ll quick(ll a, ll n, ll p) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans = ans * a % p;
        a = a * a % p;
        n >>= 1;
    }
    return ans;
}

void init() {
    phi[1] = vis[1] = 1;
    for (int i = 2; i < N; i++) {
        if (!vis[i]) {
            pri[++cnt] = i;
            phi[i] = i - 1;
        }
        for (int j = 1; j <= cnt && i * pri[j] < N; j++) {
            vis[i * pri[j]] = 1;
            if (i % pri[j] == 0) {
                phi[i * pri[j]] = phi[i] * pri[j] % p;
                break;
            }
            phi[i * pri[j]] = phi[i] * phi[pri[j]];
        }
    }
    for (int i = 1; i < N; i++) {
        sum_f[i] = (sum_f[i - 1] + 1LL * i * i % p * phi[i] % p) % p;
    }
}
unordered_map <ll, ll> mp;
ll get3(ll x) {
    x %= p;
    ll tmp = x * (x + 1) % p * inv2 % p;
    return tmp * tmp % p;
}
ll get2(ll x) {
    x %= p;
    ll ret = x * (x + 1) % p * (2 * x % p + 1) % p * inv6 % p;
    return ret;
}
ll pre_f(ll x) {
    if (x < N) return sum_f[x];
    if (mp.count(x)) return mp[x];
    ll ans = 0;
    for (ll l = 2, r; l <= x; l = r + 1) {
        r = x / (x / l);
        ans = (ans + (get2(r) - get2(l - 1)) % p * pre_f(x / l) % p);
        if (ans < 0) ans += p;
    }
    ans = (get3(x) - ans) % p;
    if (ans < 0) ans += p;
    return mp[x] = ans;
}

ll solve(ll x) {
    ll ans = 0;
    for (ll l = 1, r; l <= x; l = r + 1) {
        r = x / (x / l);
        ll t = get3(x / l);
        ans = (ans + t * (pre_f(r) - pre_f(l - 1)) % p) % p;
        if (ans < 0) ans += p;
    }
    return ans;
}

int main() {
    scanf("%lld%lld", &p, &n);
    inv2 = quick(2, p - 2, p);
    inv6 = quick(6, p - 2, p);
    init();
    ///ll ans = 0;
    ///for (ll i = 1; i <= n; i++) {
    ///    ans += get3(n / i) * i % p * i % p * phi[i] % p;
    ///    ans %= p;
    ///}
    ///cout << ans << endl;
    printf("%lld\n", solve(n));
    return 0;
}
发布了130 篇原创文章 · 获赞 80 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KXL5180/article/details/98883827
今日推荐