【题解】LibreOJ #6229 题解

题目链接

Description

给定 \(n\),求:

\[F(n)=\sum_{i=1}^n\sum_{j=1}^i\frac{\operatorname{lcm}(i,j)}{\gcd(i,j)}\bmod (10^9 + 7) \]

\(1\le n\le 10^9\)

Solution

首先,这个求和范围有些不爽,利用对称性转化一下得:

\[F(n)=\frac 12\left(n+\sum_{i=1}^n\sum_{j=1}^n\frac{\operatorname{lcm}(i,j)}{\gcd(i,j)}\right) \]

然后推一下式子:

\[\begin{aligned} \sum_{i=1}^n\sum_{j=1}^n\frac{\operatorname{lcm}(i,j)}{\gcd(i,j)}=&\sum_{i=1}^n\sum_{j=1}^n\frac{ij}{\gcd^2(i,j)}\\ =&\sum_{d=1}\sum_{i=1}^{\lfloor\frac nd\rfloor}\sum_{i=1}^{\lfloor\frac nd\rfloor}ij[\gcd(i,j)=1]\\ =&\sum_{d=1}\sum_{e=1}\mu(e)e^2\sum_{i=1}^{\lfloor\frac n{de}\rfloor}\sum_{i=1}^{\lfloor\frac n{de}\rfloor}ij\\ =&\sum_{t=1}\left(\sum_{e\mid t}\mu(e)e^2\right)\left(\frac{\lfloor\frac nt\rfloor(\lfloor\frac nt\rfloor+1)}2\right)^2 \end{aligned} \]

于是问题转化为对 \(f(n)=\sum_{d\mid n}\mu(d)d^2\) 求前缀和。

Lemma

对于数论函数 \(A,B\)完全积性函数 \(C\),有:

\[(A\cdot C)*(B\cdot C)=(A*B)\cdot C \]

证明:把式子化开:

\[\sum_{d\mid n}A(d)C(d)B(\frac nd)C(\frac nd)=C(n)\sum_{d\mid n}A(d)B(\frac nd) \]

回到 \(f(n)\)。显然有 \(f=(\mu \cdot \text{id}_2)*1\)。考虑杜教筛。两边同时卷上一个 \(\text{id}_2\)

\[\begin{aligned} (\mu\cdot \text{id}_2)*1*\text{id}_2=&(\mu\cdot\text{id}_2)*(1\cdot\text{id}_2)*1\\ =&(\mu*1)\cdot \text{id}_2*1\\ =&\epsilon *1\\ =&1 \end{aligned} \]

于是可以杜教筛。注意到计算式子需要的前缀和全都可以表示为前 \(\lfloor\frac nd\rfloor\) 项的和,记忆化一下即可。

record & code:

#include <cstdio>
#include <map>

typedef long long ll;
const ll P = 1e9 + 7,inv2 = (P + 1) / 2,inv3 = (P + 1) / 3;
const int N = 5e5,maxn = N + 5;

ll n,lst,ans;
ll pri[maxn]; bool isp[maxn];
ll mu[maxn],pre[maxn];
std::map < ll,ll > mem;

inline ll read() {
#define gc c = getchar()
    ll d = 0; int f = 0,gc;
    for(;c < 48 || c > 57;gc) f |= (c == '-');
    for(;c > 47 && c < 58;gc) d = (d << 1) + (d << 3) + (c ^ 48);
#undef gc
    return f ? -d : d;
}

inline ll get1(ll n) { n = n * (n + 1) / 2 % P; return n * n % P; }
inline ll get2(ll n) { return n * (n + 1) / 2 % P * (2 * n + 1) % P * inv3 % P; }

inline void Init() {
    mu[1] = 1;
    for(ll i = 2;i <= N;i ++) {
        if(!isp[i]) pri[++ pri[0]] = i,mu[i] = -1;
        for(ll j = 1;j <= pri[0] && i * pri[j] <= N;j ++) {
            isp[i * pri[j]] = true;
            if(!(i % pri[j])) break;
            mu[i * pri[j]] = -mu[i];
        }
    }
    for(ll t,i = 1;i <= N;i ++) {
        if(!mu[i]) continue;
        t = i * i % P; t = mu[i] == 1 ? t : P - t;
        for(ll j = 1;i * j <= N;j ++)
            pre[i * j] = (pre[i * j] + t) % P;
    }
    for(ll i = 1;i <= N;i ++) pre[i] = (pre[i] + pre[i - 1]) % P;
}

inline ll getSum(ll n) {
    if(n < N) return pre[n];
    if(mem.count(n)) return mem[n];
    ll res = n,cur = 0;
    for(ll r,l = 2;l <= n;l = r + 1) {
        r = n / (n / l);
        cur = (cur + (get2(r) - get2(l - 1) + P) * getSum(n / l) % P) % P;
    }
    return mem[n] = (res - cur + P) % P;
}

int main() {
    n = read(); Init();
    for(ll tmp,r,l = 1;l <= n;l = r + 1) {
        r = n / (n / l);
        tmp = getSum(r);
        ans = (ans + (tmp - lst + P) * get1(n / l) % P) % P;
        lst = tmp;
    }
    printf("%lld\n",(ans + n) * inv2 % P);
    return 0;
}
posted @ 2022-01-19 23:03  Sya_Resory  阅读(19)  评论(0编辑  收藏  举报