题意
求 $sum_{i=1}^n sum_{j=1}^n gcd(i,j)$.
分析
$$egin{aligned}
sum_{i=1}^n sum_{j=1}^n gcd(i,j) &= sum_{i=1}^n sum_{j=1}^n d[gcd(i, j)=d] \
&= sum_{d=1}^n d sum_{i=1}^n sum_{j=1}^n[gcd(i,j=d)] \
&= sum_{d=1}^n d sum_{i=1}^{left lfloor frac{n}{d}
ight
floor}mu (i) left lfloor frac{n}{id}
ight
floorleft lfloor frac{n}{id}
ight
floor \
&= sum_{T=1}^n ({frac{n}{T}})^2 sum_{d|T}dmu (frac{T}{d}) \
&= sum_{T=1}^n ({frac{n}{T}})^2 varphi (T)
end{aligned}$$
使用了几个套路,
一是 $gcd(i,j)=d$ 的经典求和式子;
二是出现 $sum_{d=1}^n d sum_{i=1}^{left lfloor frac{n}{d} ight floor}$,枚举 $T=id$;
三是使用结论 $mu * ID = varphi$.
求欧拉函数的前缀和可以使用杜教筛。
#include<bits/stdc++.h> using namespace std; const int maxn = 2000010; typedef long long ll; const ll mod = 1000000007; const ll inv2 = (mod+1)>>1; ll T, n, pri[maxn], tot, phi[maxn], sum_phi[maxn]; bool vis[maxn]; unordered_map<ll, ll> mp_phi; //可换成unordered_map ll S_phi(ll x) { if (x < maxn) return sum_phi[x]; if (mp_phi[x]) return mp_phi[x]; ll ret = (x%mod) * ((x+1)%mod) % mod * inv2 % mod; for (ll i = 2, j; i <= x; i = j + 1) { j = x / (x / i); ret =(ret - S_phi(x / i) * (j - i + 1) % mod + mod) % mod; } return mp_phi[x] = ret; } void initPhi() { phi[1] = 1; for (int i = 2; i < maxn; i++) { if (!vis[i]) pri[++tot] = i, phi[i] = i-1; for (int j = 1; j <= tot && i * pri[j] < maxn; j++) { vis[i * pri[j]] = true; if (i % pri[j] == 0) { phi[i * pri[j]] = phi[i] * pri[j] % mod; break; } else { phi[i * pri[j]] = phi[i] * phi[pri[j]] % mod; } } } for (int i = 1; i < maxn; i++) sum_phi[i] = (sum_phi[i - 1] + phi[i]) % mod; } void solve() { ll res = 0; for(ll i = 1, j;i <= n;i = j+1) { j = n / (n / i); ll tmp = (n/i) % mod; res = (res + tmp * tmp % mod * (S_phi(j)-S_phi(i-1) + mod) % mod) % mod; } printf("%lld ", res); } int main() { initPhi(); scanf("%lld", &n); solve(); return 0; }
参考链接: