洛谷P2398 GCD SUM题解
思路
考虑欧拉函数有个性质:
\(\sum\limits_{d|n}\varphi(d)=n\)
那么在这道题里面我们把\(\gcd(i,j)\)当做\(n\) 带入,然后开始愉快的推式子!(我的步骤很详尽的!)...
所以:
\(\ \ \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\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{d|i,d|j}\varphi(d)\)
=\(\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{d=1}^n\varphi(d)[d|i][d|j]\)
=\(\sum\limits_{d=1}^n\varphi(d)\sum\limits_{i=1}^n\sum\limits_{j=1}^n[d|i][d|j]\)
=\(\sum\limits_{d=1}^n\varphi(d)\sum\limits_{i=1}^n[d|i]\sum\limits_{j=1}^n[d|j]\)(这一步到下一步是如何化简的,其实这两个\(\sum\)就是在求\([1,n]\)中有多少个是\(d\)的倍数了,那么只要您学过小学奥术,那么就可以愉快地看懂如何化简的了!)
=\(\sum\limits_{d=1}^n\lfloor\frac{n}{d}\rfloor^2\)
那么这个式子很显然,\(\lfloor\frac{n}{d}\rfloor^2\)可以用整除分快做,如果不会的话,此题数据小,可以暴力过,但是墙裂推荐整除分块,又短又快!
\(ACcode:\)
#include<bits/stdc++.h>
#define F(i,l,r) for(register int i=l;i<=r;i++)
#define D(i,r,l) for(register int i=r;i>=l;i--)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define p_b push_back
#define m_p make_pair
#define il inline
const int INF=0x3f3f3f3f,N=1e5+5;
using namespace std;
int prime[N],phi[N];
ll sum[N],ans;//记得开ll!
bool isp[N];
int n;
void sieve(int n) {
int num=0;
phi[1]=1;
F(i,2,n) isp[i]=1;
F(i,2,n) {
if(isp[i]) prime[++num]=i,phi[i]=i-1;
F(j,1,num) {
if(prime[j]*i>n) break;
isp[prime[j]*i]=false;
if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1);
else {
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
}
}
F(i,1,n) sum[i]=sum[i-1]+phi[i];
}//这里是线性递推欧拉函数的值,然后如果要做整除分块的话,是要求出前缀和才可做的。
int main() {
scanf("%d",&n);
sieve(n);
for(int i=1,j;i<=n;i=j+1) {
j=n/(n/i);
ans+=(sum[j]-sum[i-1])*(n/i)*(n/i);//整除分块
}
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号