[c++]gcd(數論)

自己的一個小結

題目描述

給定整數N,求1<=x,y<=N且Gcd(x,y)爲素數的數對(x,y)有多少對?

輸入

一個整數
1<=N<=1000000

輸出

一個整數

樣例輸入

4

樣例輸出

4

提示

【樣例解釋】
(2,2),(2,4),(3,3),(4,2)

思路

本文所有的P,均表示質數)
對於x,y 1 <= x,y <= n,且gcd(x,y) = P
由此,我們很容易相當歐拉篩法(Euler)

φφ:是小於n的正整數中與n互質的數的數目(φφ(1) == 11

註明:源自百度
我們假設gcd(xx,yy) == 11,則一定有 gcd(xx ×\times P,yy ×\times P) == P
我們不妨設 xx <= yy,則我們可以得出 11 <= xx <= yy <= nP{n} \over {P}
所以我們可以先花( OnO(n))的時間算出φφ
然後我們只需要將 1 - (nP{n} \over {P})以內的所有φφ值乘二減一後加起來即可,(這些數同時乘以P後,不就是gcd(xx ×\times P,yy ×\times P) == P了嗎)
數學表達:i=1totj=1nPans+=φ(j)×21\sum_{i = 1}^{tot}\sum_{j = 1}^{{n} \over {P}}ans+=φ(j) \times 2 - 1
乘二是因爲我們假設的是 xx <= yy 的情況,然而實際上,將xx,yy的值對調又可以對答案做出貢獻,可是我們還需要減去一個一,這是因爲(1 ×\times P,1 ×\times P)= P顯然只有一組,所以我們需要減去一個一
總而言之,這裏可用前綴和思想,但是好像空間過不去……

看看代碼有利於理解

#include<cstdio>
#define LL long long
#define reg register
#define M 10000000
 
int n,tot;
 
LL ans;
int phi[M + 5],prime[M + 5];
bool vis[M + 5];
 
void Euler(int N){
    phi[1] = 1;
    for (reg int i = 2;i <= N; ++ i){
        if ( ! vis[i]){
            prime[ ++ tot] = i;
            phi[i] = i - 1;
        }
        for (reg int j = 1;j <= tot; ++ j){
            if (prime[j] * i > N)
                break;
            vis[prime[j] * i] = true;
            if (i % prime[j] == 0){
                phi[i * prime[j]] = prime[j] * phi[i];
                break;
            } else
                phi[i * prime[j]] = (prime[j] - 1) * phi[i];
        }
    }
}
int main(){
    scanf("%d",&n);
    Euler(n);
    for (reg int i = 1;i <= tot; ++ i){
        int g = n / prime[i];
        LL sum = 0;
        for (reg int j = 1;j <= g; ++ j)
            sum += 1ll * phi[j];
        ans += (sum * 2 - 1);
    }
    printf("%lld\n",ans);
    return 0;
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章