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