歐拉篩擴展

歐拉篩可以認爲是埃篩的升級版。

埃篩的缺陷在於對於一個合數,有可能被篩多次,例如30=2*15=3*10=5*6。不難發現,如果我們用它的最小質因子來篩選,就可以保證每個合數只被篩選一次,這便是歐拉篩法。

而歐拉篩的擴展就在於它可以在篩素數的同時求積性函數。

積性函數定義:對於一個函數f(x)滿足:若gcd(a,b)=1,則f(a*b)=f(a)*f(b)。

求歐拉函數

int prime[maxn];
int mindiv[maxn];
int phi[maxn];
int cnt;
void solve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!mindiv[i])
        {
            mindiv[i]=prime[++cnt]=i; phi[i]=i-1;
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            mindiv[k]=prime[j];
            if(i%prime[j]==0)
            {
                phi[k]=phi[i]*prime[j];
                break;
            }
            phi[k]=phi[i]*(prime[j]-1);
        }
    }
}

求最小質因數的指數

int prime[maxn];
int mindiv[maxn];
int minexp[maxn];
int cnt;
void solve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!mindiv[i])
        {
            mindiv[i]=prime[++cnt]=i;
            minexp[i]=1;
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            mindiv[k]=prime[j];
            if(i%prime[j]==0)
            {
                minexp[k]=minexp[i]+1;
                break;
            }
            minexp[i]=1;
        }
    }
}

求約數個數

int prime[maxn];
int mindiv[maxn];
int minexp[maxn];
int d[maxn];
int cnt;
void solve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!mindiv[i])
        {
            mindiv[i]=prime[++cnt]=i;
            minexp[i]=1;
            d[i]=2;
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            mindiv[k]=prime[j];
            if(i%prime[j]==0)
            {
                minexp[k]=minexp[i]+1;
                d[k]=d[i]/(minexp[i]+1)*(minexp[k]+1);
                break;
            }
            minexp[i]=1;
            d[k]=d[i]*d[prime[j]];
        }
    }
}

求約數個數還有個野路子

for(int i=1;i<=n;i++)
    for(int j=i;j<=n;j+=i)
        d[j]++;

雖然速度不及歐拉篩,但它簡單好寫啊。

求約數和

int prime[maxn];
int mindiv[maxn];
int sumd[maxn];
int f1[maxn];//f1[i]=sigma(mindiv[i]^(0..minexp[i])) 
int f2[maxn];//f2[i]=mindiv[i]^minexp[i]
int cnt;
void solve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!mindiv[i])
        {
            mindiv[i]=prime[++cnt]=i;
            sumd[i]=1+i;
            f1[i]=1+i;
            f2[i]=i;
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            mindiv[k]=prime[j];
            if(i%prime[j]==0)
            {
                f2[k]=f2[i]*prime[j];
                f1[k]=f1[i]+f2[k];
                sumd[k]=sumd[i]/f1[i]*f1[k];
                break;
            }
            sumd[k]=sumd[i]*sumd[prime[j]];
            f1[k]=1+prime[j];
            f2[k]=prime[j];
        }
    }
}

求莫比烏斯函數

int prime[maxn];
int mindiv[maxn];
int miu[maxn];
int cnt;
void solve(int n)
{
    miu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!mindiv[i])
        {
            mindiv[i]=prime[++cnt]=i;
            miu[i]=-1;
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            mindiv[k]=prime[j];
            if(i%prime[j]==0)
            {
                miu[k]=0;
                break;
            }
            miu[k]=-miu[i];
        }
    }
}

以上都是比較常見的積性函數,用篩法求積性函數的本質是根據積性函數的定義和運算式,利用篩法的特點,構造合適的轉移方法。

這裏給出基本模板

void solve(int n)
{
    v[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!v[i])
        {
            prime[++cnt]=i;
            .....//素數的積性函數值
        }
        for(int j=1,k;j<=cnt&&(k=i*prime[j])<=n;j++)
        {
            v[i*prime[j]]=1;
            .....//公共部分
            if(i%prime[j]==0)
            {
                .....//k和k的最小素因子的處理
                break;
            }
            .....//k和比k的最小素因子小的素數的處理
        }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章