Min_25篩--簡明版

強烈推薦鏈接

 

一.前置技能

  • 埃式篩法:標記素數的倍數

(線性篩是標記每個數的素數倍數)

  • 積性函數性質:\bg_white f(n)=f(n/p)*f(p) (f(1)=1)

(積性函數比如歐拉函數需要條件gcd(a,b)=1,完全積性函數不需要)

 

二.適用範圍

min2.5篩:質數冪的多項式(完全積性函數)

\sum {p^k}

(杜教篩:可以利用狄利克雷卷積轉換爲數論函數,方便求前綴和)

顯然min2.5篩似乎適用更廣些

時間複雜度:\mathcal O(\frac{n^\frac{3}{4}}{\log n})   空間複雜度:\mathcal O(\sqrt{n})

 

三.算法思路

首先我們定義:

f(i)=i^k

g(n,j)=\sum_{i=2}^nf(i) [i\subseteq prime \quad or \quad {min_np>prime_j}] (min_np\quad is \quad the \quad smallest \quad prime \quad in\quad n)

 

g(n,0)表示不對合數做限制,因此是所有數的k次冪的和

但我們要只保留所有素數k次冪的和

最終得到g(n,INF)

每一輪類似埃式篩法的過程篩掉不滿足的數,g(n,j)便代表:對 2~n做埃拉託斯特尼篩法 ,j 輪後剩下的所有數的 k 次冪和

注意!g(n,j)包括 prime1,..primej 這些處理過的質數。

 

每輪從j-1→j,篩掉了f(i)

類似埃式篩法,我們只需篩n>=primej^2的部分,過程如下:

 

①n<=primej^2的部分已經在前面幾輪被篩掉,f(i)=0

 

②n>=primej^2,則要篩掉大於的部分,這部分可以用積性函數性質:

f(i)=f(i/prime_j)f(prime_j)

 

f(i/primej)可以用g表示,因爲n/primej>=n,所以g已經包含了前j-1輪已經篩出來的質數部分,減多了這部分要加回來

 

③先算出\sqrt{n}內質數冪的和的式子

 

g(n,j)= \begin{cases} g(n,j-1), & n<prime_j^2\\ g(n,j-1)-prime_j^k\left(g(\lfloor \frac{n}{prime_j} \rfloor,j-1)-g(prime_{j-1},j-1)\right), & n\ge prime_j^2 \end{cases}

 

 

④最終計算:

我們定義:(注意沒有or了,即使是質數,也受到本身>primeb約束)

S(a,b)=\sum_{i=2}^a [pmin_i\ge prime_b]f(i)

 

記憶化

S(a,b)= \begin{cases} S(a,b+1), & a<prime_b^2 \\ \\ S(a,b+1) + \\ \sum\limits_{prime_b^{i+1} \le a} \left(f(prime_b^i)* \left(S(\lfloor \frac{a}{prime_b^i} \rfloor, b+1) - g(prime_b, \infty)\right) + f(prime_b^{i+1}) \right), & a\ge prime_b^2 \end{cases}

 

不記憶化

若是質數,利用篩出了\sqrt{n}的質數和(注意最小質數要求大於b,所以要減去小於b的部分)(g-g那部分)

若是合數,枚舉最小質因子的次數,累加即可(前半部分是除掉最小質數其他合數的貢獻,後半部分是該質數的t次方的貢獻)

S(a,b)= \begin{cases} 0, & a<prime_b\\ \\ g(a,\infty)-g(prime_{b-1},\infty)+ \\ \sum\limits_{i=b}^{\infty} \sum\limits_{t\ge 1, prime_i^{t+1}\le a}\left( S(\lfloor \frac{a}{prime_i^t} \rfloor,i+1) * f(prime_i^t) + f(prime_i^{t+1}) \right), &a\ge prime_b \end{cases}

 

四.代碼

推薦參考鏈接

遞歸版

遞推版

(暫)參考洛谷模板,注意預處理n/i的值以及加上f(1)的值

 

#include<bits/stdc++.h>
#define il inline
#define vd void
#define mod 1000000007
typedef long long ll;
il ll gi(){
    ll x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))f^=ch=='-',ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f?x:-x;
}
ll n;
int qt;
int pr[100010],yes[100010],cnt,gp1[100010],gp2[100010];
ll w[200010],sw;
int g1[200010],g2[200010];
int id1[100010],id2[100010];//預處理n/i的值,分別小於大於根號n
il int S(ll x,int y){
    if(pr[y]>=x)return 0;
    int p=x<=qt?id1[x]:id2[n/x];
    int ret=((0ll+g2[p]-g1[p]-(gp2[y]-gp1[y]))%mod+mod)%mod;//p^k^2-p^k
    for(int i=y+1;i<=cnt&&1ll*pr[i]*pr[i]<=x;++i){
        ll pe=pr[i];
        for(int e=1;pe<=x;++e,pe*=pr[i]){
            int o=pe%mod;
            ret=(ret+1ll*o*(o-1)%mod*(S(x/pe,i)+(e!=1)))%mod;
        }
    }
    return ret;
}
int main(){
    n=gi();qt=sqrt(n);
    yes[1]=1;
    for(int i=2;i<=qt;++i){
        if(!yes[i])pr[++cnt]=i;
        for(int j=1;j<=cnt&&i*pr[j]<=qt;++j){
            yes[i*pr[j]]=1;
            if(i%pr[j]==0)break;
        }
    }
    for(int i=1;i<=cnt;++i)gp1[i]=(gp1[i-1]+pr[i])%mod,gp2[i]=(gp2[i-1]+1ll*pr[i]*pr[i])%mod;//遞推時用的質數處F前綴和
    for(ll l=1,r;l<=n;l=r+1){//預處理(n/i) & 計算g的j=0邊界,邊界就是F'的前綴和,由於質數處F(p)是多項式所以可以快速算
        r=n/(n/l);w[++sw]=n/r;
        g1[sw]=w[sw]%mod;
        g2[sw]=(1ll*g1[sw]*(g1[sw]+1)%mod*(g1[sw]*2+1)%mod*166666668%mod-1)%mod;//1..w[sw]平方和
        g1[sw]=(1ll*g1[sw]*(g1[sw]+1)%mod*500000004-1)%mod;//1..w[sw]等差數列和
        if(n/r<=qt)id1[n/r]=sw;else id2[r]=sw;
    }
    //j從1到|P|遞推g
    for(int i=1;i<=cnt;++i){
        ll sqr_pi=1ll*pr[i]*pr[i];
        for(int j=1;j<=sw&&sqr_pi<=w[j];++j){
            ll p=w[j]/pr[i];
            p=(p<=qt?id1[p]:id2[n/p]);//定位p的座標
            g1[j]=(g1[j]-1ll*pr[i]*(g1[p]-gp1[i-1]+mod)%mod+mod)%mod;
            g2[j]=(g2[j]-1ll*pr[i]*pr[i]%mod*(g2[p]-gp2[i-1]+mod)%mod+mod)%mod;
        }
    }
    printf("%d\n",(S(n,0)+1)%mod);
    return 0;//}

 

 

 

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