1. 程式人生 > 實用技巧 >【POJ2480】Longge's problem

【POJ2480】Longge's problem

題目連結

Longge's problem

題目描述

Longge is good at mathematics and he likes to think about hard mathematical problems which will be solved by some graceful algorithms. Now a problem comes: Given an integer \(N\)(\(1 < N < 2^{31}\)),you are to calculate \(\sum gcd\)(\(i, N\)) \(1 \le i \le N\).

"Oh, I know, I know!" Longge shouts! But do you know? Please solve it.

輸入格式

Input contain several test case.
A number \(N\) per line.

輸出格式

For each \(N\), output ,\(\sum gcd\)(\(i,N\)) \(1 \le i \le N\), a line

樣例輸入

2
6

樣例輸出

3
15

題解

題目要求\(\sum_{i=1}^ngcd\)(\(i,n\))
那麼我們考慮一個數\(i\)對答案的貢獻,也就是\(gcd\)(\(k,n\))\(==i\)的個數乘上\(i\)
首先,如果\(i\)不是\(n\)的因數,那麼不可能有\(gcd\)(\(k,n\))\(==i\)

,所以\(i\)對答案的貢獻為\(0\)
那麼如果\(i\)\(n\)的因子,那麼如果存在\(gcd\)(\(k,n\))\(==i\),那麼\(k\)一定滿足\(k/i\)\(n/i\)互質,也就是說\(k\)\(n\)的因子中除去\(i\)後沒有相同的因子了。
那麼我們馬上就發現了\(i\)對答案的貢獻就是\(i*\phi\)(\(n/i\))。
那麼顯然題目就變成了求\(\sum_{i|n}i*\phi\)(\(n/i\))

在判斷質數的時候我們都學過一個數的因子都是“對稱”的,所以在列舉\(\phi\)(\(n/i\))的過程中我們只要列舉到\(\sqrt{n}\),就行了。

接下來的目標就是如何快速求\(\phi\)

(\(i\))了,
我們先了解尤拉函式的一些性質:
有一個質數\(p\),則
\(\phi\)(\(p^k\))\(=p^k-p^{k-1}=p^{k-1}*(p-1)\)
因為\(p^k\)以內不和\(p^k\)互質的數只有\(p\)的倍數,也就是數量為\(\frac{p^k}{p}\)
因為\(\phi\)是積性函式,所以令\(p,k\)互質,則
\(\phi\)(\(p*k\))\(=\phi\)(\(p\))\(*\phi\)(\(k\))
所以我們就能用下面的程式碼求\(\phi\)

inline long long phi(long long x){
    long long sum=x;
    for(long long j=2;j*j<=x;j++)
        if(x%j==0){
            while(x%j==0) x/=j;
            sum=sum/j*(j-1);
        }
    if(x!=1) sum=sum/x*(x-1);
    return sum;
}

ps:有多組資料
上程式碼:

#include<cstdio>
using namespace std;
long long n;
inline long long fd(long long x){
    long long sum=x;
    for(long long j=2;j*j<=x;j++){
        if(x%j==0){
            while(x%j==0) x/=j;
            sum=sum/j*(j-1);
        }
    }
    if(x!=1) sum=sum/x*(x-1);
    return sum;
}
long long sum;
int main(){
    while(scanf("%lld",&n)!=EOF){
        sum=0;
        for(long long i=1;i*i<=n;i++){
            if(n%i==0) sum+=fd(n/i)*i;
            long long u=n/i;
            if(u!=i && n%u==0) sum+=fd(n/u)*u;
        }
        printf("%lld\n",sum);
    }
    return 0;
}