欧拉反演
欧拉反演
求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\gcd(i,j)\) .
欧拉函数性质:
\(\sum\limits_{d|n}\varphi(d) = n\)
优化和式技巧:
\(\sum\limits_{i=1}^n\sum\limits_{d|i}x=\sum\limits_{d=1}^n[\frac{n}{d}]x\)
有了以上两个东西,那么:
\(\begin{aligned} &\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\gcd(i,j)\\&= \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{d|\gcd(i,j)}\varphi(d) \\&=\sum_{i=1}^n\sum_{j=1}^n\sum_{d|i}\sum_{d|j}\varphi(d)\\&=\sum_{d=1}^n\varphi(d)*[n/d][n/d]\\&=\sum_{d=1}^n\varphi(d)[\frac{n}{d}]^2\end{aligned}\)
筛一下 \(\varphi(d)\) 求前缀和 \(O(n)\),然后整出分块 $O(\sqrt n) $ 就完成了
#include<bits/stdc++.h>
#define int long long //不开long long 真的会死的
using namespace std;
const int maxn=1e5+7;
bool vis[maxn]; int cnt,prim[maxn],phi[maxn],n,ans;
void init()
{
phi[1]=1;
for(int i = 2; i <= maxn; ++i) {
if(!vis[i]) {
prim[++cnt] = i; phi[i] = i - 1;
}
for(int j = 1; j <= cnt && prim[j] * i <= maxn; ++j) {
vis[prim[j] * i] = 1;
if(i%prim[j]==0) {
phi[prim[j] * i] = phi[i] * prim[j]; break;
}
phi[prim[j] * i] = phi[i] * phi[prim[j]];
}
}
for(int i=2;i<=maxn;++i) phi[i] += phi[i-1];
}
signed main() {
init();
cin>>n;
for(int l=1,r;l<=n;l=r+1) {
int t = n / l;
r = n / t;
ans += (phi[r]-phi[l-1]) * t * t;
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号