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; }