BZOJ 2226 【SPOJ 5971】 LCMSum

题目链接:LCMSum

  这个题显然就是要我们推式子了……那么就来推一波:

\begin{aligned}
&\sum_{i=1}^n lcm(i,n) \\
=&\sum_{i=1}^n\frac{ni}{\gcd(i,n)} \\
=&n\sum_{d|n}\sum_{i=1}^{\frac{n}{d}}\frac{di[\gcd(i,\frac{n}{d})=1]}{d} \\
=&n\sum_{d|n}\sum_{i=1}^{d}i[\gcd(i,d)=1]
\end{aligned}

  然后我们定义一个函数\(f(n)\):

\begin{aligned}
f(n)=&\sum_{i=1}^ni[\gcd(i,n)=1] \\
=&\sum_{i=1}^n i \sum_{d|i , d|n} \mu(d) \\
=&\sum_{d|n}\mu(d)\sum_{i=1}^{\frac{n}{d}}di \\
=&\sum_{d|n}\mu(d)d\frac{(\frac{n}{d}+1)\frac{n}{d}}{2} \\
=&\frac{n}{2}\sum_{d|n}\mu(d)(1+\frac{n}{d}) \\
=&\frac{n}{2}([n=1]+\varphi(n))
\end{aligned}

  其实\(f(n)\)还有一种更简单的求法。当\(n > 2\)时,我们可以从\(\gcd(i,n)=1\)推出\(\gcd(n-i,n)=1\),并且可以推出\(i \neq n-i\)。所以可以直接得到:

\[f(n)=\frac{n\varphi(n)}{2}(n > 2)\]

  所以我们就可以在\(O(n)\)的时间内求出所有的\(f(n)\)了。然后用算贡献的方式即可在\(O(n\log n)\)的时间内算出所有的\(\sum_{d|n}f(d)\)。

  然后这道题就做完了。总复杂度\(O(n\log n + T)\)。

  下面贴代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 1000010

using namespace std;
typedef long long llg;

int T,n,phi[maxn],pr[maxn],lp;
llg f[maxn],g[maxn]; bool vis[maxn];

int getint(){
	int w=0;bool q=0;
	char c=getchar();
	while((c>'9'||c<'0')&&c!='-') c=getchar();
	if(c=='-') c=getchar(),q=1;
	while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
	return q?-w:w;
}

int main(){
	File("a");
	T=getint(); f[1]=phi[1]=1;
	for(int i=2;i<maxn;i++){
		if(!vis[i]) pr[++lp]=i,phi[i]=i-1;
		for(int j=1;pr[j]*i<maxn;j++){
			vis[pr[j]*i]=1;
			if(i%pr[j]) phi[pr[j]*i]=phi[i]*(pr[j]-1);
			else{phi[pr[j]*i]=phi[i]*pr[j]; break;}
		}
	}
	for(int i=2;i<maxn;i++) f[i]=1ll*phi[i]*i>>1;
	for(int i=1;i<maxn;i++)
		for(int j=i;j<maxn;j+=i)
			g[j]+=f[i];
	while(T--){
		n=getint();
		printf("%lld\n",n*g[n]);
	}
	return 0;
}
posted @ 2017-02-28 09:46  lcf2000  阅读(258)  评论(0编辑  收藏  举报