【题解】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;
}