POJ2480 歐拉函數的應用

POJ 2480 歐拉函數的應用


題目鏈接

  • 乍一看和歐拉函數沒什麼關係,但數論就是這樣,處處聯繫。我們可以枚舉i(1<= i <=n),如果i|n,即i是n的因子,那麼答案加上euler(n/i)*i。其實ans = Σi*euler(n/i)(i<=i<=n && i|n)。爲什麼是這樣?比如,1到n中有m個數字和n擁有最大公因數i,那麼就需要把m*i加入答案中。問題是如何計算m的個數。如果gcd(x,n) = i,可以得到gcd(x/i , n/i)=1,也就是說,有多少個小於等於n的x滿足gcd(x/i , n/i)=1,就有多少個小於等於n的x滿足gcd(x , n)=i。那麼有多少個小於等於n的x滿足gcd(x/i , n/i)=1呢?根據歐拉函數定義,有euler(n/i)個,即m=euler(n/i),所以ans = Σi*euler(n/i)。
    然後還可以優化。小於n且與n互素的數i滿足gcd(i,n)=1,所有這樣的i的個數就是euler(n),這些數和n的最大公約數的和也就是euler(n),另外因爲gcd(n,n)=n,所以答案再加上n。這些都是枚舉之前的處理,寫成代碼就是ans=euler(n)+n。枚舉的時候,要注意只枚舉到sqrt(n),多了就會超時,大於sqrt(n)而小於n的數p在枚舉到i=n/p(當然此時n/p<=sqrt(n)<=p)就應一起被計算進答案中
#include <iostream>

using namespace std;
typedef long long ll;

ll euler(ll n)
{
    ll rea=n;
    for(ll i=2; i*i<=n; i++)
        if(n%i==0)//第一次找到的必爲素因子
        {
            rea=rea-rea/i;
            do
                n/=i;//把該素因子全部約掉
            while(n%i==0);
        }
    if(n>1)
        rea=rea-rea/n;
    return rea;
}
int main()
{
    ll n;
    while(cin>>n){
        ll s=n+ll(euler(n));
        for(ll i=2;i*i<=n;++i){
            if(n%i==0){
                if(i*i==n) s+=i*euler(i);
                else s+=(i*euler(n/i)+n/i*euler(i));
            }
        }
        cout<<s<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章