poj 2478 Farey Sequence 線性篩法優化的歐拉函數

一:題意:
給定一個數n,求在[1,n]這個範圍內兩兩互質的組合數,該題其實就是求,[2,n]分區間內的所以數組的歐拉函數之和,其中n (2 <= n <= 106)。
二:歐拉函數的概念
1,歐拉函數:
對於一個正整數n,小於n且和n互質的正整數(包括1)的個數,叫做該數的歐拉函數,記作φ(n) 。例如φ(8)=4,因爲1,3,5,7均和8互質。
2,歐拉函數計算:
(1) 通式:φ(x)=x * (1-1/p1) * (1-1/p2) * (1-1/p3)…… (1-1/pn),其中p1, p2……pn爲x的所有質因數,x是不爲0的整數。
(2) 特例:φ(1)=1。
(3) 注意:每種質因數只一個。比如12=2*2*3那麼φ(12)=12*(1-1/2)*(1-1/3)=4
3,歐拉函數的性質:
(1) 若n是質數p的k次冪,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1)。因爲除了p的倍數外,其他數都跟n互質。
(2) 若m,n互質,φ(mn)=φ(m)φ(n)。
(3) 當n爲奇數時,φ(2n)=φ(n).
歐拉打表最重要的兩個性質:
(4) 對於質數p,φ(p) = p - 1。 注意φ(1)=1.
(5) 設a爲N的質因數,
若(N % a == 0 && (N / a) % a == 0) 則有: E(N)=E(N / a) * a;
若(N % a == 0 && (N / a) % a != 0) 則有:E(N) = E(N / a) * (a - 1)。
這裏可以看出p和a都是質數(素數),所以我們可以利用素數的先行篩選法來加快歐拉打表。
三:素數的線性篩選

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
const int Max=100000;
bool isPrime[Max+2];// 標記素數
int prime[Max+2];//記錄素數
int total=0;//素數個數記錄
void Prime()
{
    memset(isPrime,true,sizeof(isPrime));
    total=0;
    for(int i=2; i<=Max; i++)
    {
        if(isPrime[i])
            prime[total++]=i;//放入表中
        for(int j=0; j<total && i*prime[j]<=Max; j++)
        {
            isPrime[i*prime[j]]=false;//利用合數必能分解出一個素數
            if(i%prime[j]==0)
                break;//防止重挖
        }
    }
    printf("%d\n",total);
}
int main()
{
    Prime();
    return 0;
}

四:線性歐拉打表代碼(poj2478):

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const int Max=1000010;
long long str[Max];//歐拉表
int used[Max];//素數標記
int prime[Max];//素數記錄
int N,Size;

void print()
{//線性打表
    memset(used,true,sizeof(used));
    Size=0;
    for(int i=2;i<=1000000;i++)
    {
        if(used[i])
        {
            str[i]=i-1;//i是素數的情況
            prime[Size++]=i;
        }
        for(int j=0;j<Size&& i*prime[j]<1000002;j++)
        {
            used[i*prime[j]]=false;
            if(i%prime[j])//歐拉兩個打表定理
                 str[prime[j]*i]=str[i]*(prime[j]-1);
            else
            {
                 str[prime[j]*i]=str[i]*prime[j];
                 break;
            }
        }
    }
    for(int i=3;i<=1000000;i++)
        str[i]=str[i]+str[i-1];
}

int main()
{
    print();
    while(scanf("%d",&N)!=EOF && N)
    {
        printf("%lld\n",str[N]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章