洛谷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;
}
posted @ 2020-10-06 22:57  moonlateQZ  阅读(115)  评论(0)    收藏  举报