BZOJ 2005 [Noi2010]能量采集
2017.12.07
2017.12.07
求$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} (\gcd(i,j)-1)$.
需要用到的公式:$\sum\limits_{d|n} \varphi(d) = n$
难度适中的一道数论题= =
"-1"没啥用可以先去掉。之后就变成$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} \gcd(i,j)$.
第一次变换:进行欧拉反演。 第二次变换:将$\varphi(d)$提出来统计贡献。
$$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} \gcd(i,j) = \sum\limits_{i=1}^{n} \sum\limits_{j=1}^{m} \sum\limits_{d|\gcd(i,j)} \varphi(d) = \varphi(d) \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor$$
之后枚举d就可以了。
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,prm[100001],cnt,phi[100001];
long long ans;
bool v[100001];
void init()
{
int cnt=0;
phi[1]=1;
for(int i=2;i<=n;++i)
{
if(!v[i]) prm[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&i*prm[j]<=n;++j)
{
v[i*prm[j]]=true;
if(i%prm[j]) phi[i*prm[j]]=phi[i]*(prm[j]-1);
else
{
phi[i*prm[j]]=phi[i]*prm[j];
break;
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
if(n>m) swap(n,m);
init();
for(int i=1;i<=n;++i)
ans+=1ll*phi[i]*(n/i)*(m/i);
printf("%lld\n",ans*2-1ll*n*m);
}