51nod 1040(欧拉函数)

给出一个n,求1-n这n个数,同n的最大公约数的和。比如:n = 6

1,2,3,4,5,6 同6的最大公约数分别为1,2,3,2,1,6,加在一起 = 15

输入
  1个数N(N <= 10^9)
输出
  最大公约数之和

输入样例
  6
输出样例
  15




思路:n最大公约数之和(用sum表示)是有他的因数求得,我们只要求得对应因数有多少个。 
比如 6 因数: 1 2 3 6
   个数: 2 2 1 1
答案就是 1*2+2*2+3*1+6*1


怎么求个数呢?
如果如果暴力求解我们会这样做:遍历1~n求每个最大公约数相加
即 for(int i=1;i<=n;i++)
sum+=gcd(i,n)
很显然会超时。


我们假设 gcd(i,n)=v (i是1~n) v一定是n的因数。
我们要求的就是v的个数。
可以转换成求gcd(i/v,n/v)=1个数 (i/v是0~n/v ) n/v也是n的因数
答案就是sum(p*phi(n/p))了(p是因子,phi()是欧拉函数,sum是求和)。

 
代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
ll eular(ll n)//欧拉函数模板 
{
    ll ans=n;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            ans-=ans/i;
            while(n%i==0)
            n/=i;
        }
    }
    if(n>1)
        ans-=ans/n;
    return ans;
}
int main()
{
    ll n;
    while(cin>>n)
    {
        ll ans=0;
        for(ll i=1;i*i<=n;i++)
        {
            if(n%i==0)//i是n的因子 
            {
                ans+=i*eular(n/i);//因子v 乘以个数 (1-n/v与n/v互质的个数) 
                if(n!=i*i)//  n/v是n的因子,不要漏算了 
                    ans+=n/i*eular(i);
            } 
        }
        cout<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiongtao/p/9323777.html