埃氏篩法+線性篩法+杜教篩+min25篩總結
埃氏篩法
這個篩法是最樸素的篩法了,可以在
的時間內(基本
)篩出[1,n]中所有素數。實現非常簡單,從2開始遍歷,對於每個質數都暴力算出它的所有倍數並篩掉,根據尤拉的調和級數定理,這個時間是不知怎麼回事複雜度就變成
了。
const int maxn = 100005, N = 100000;
int vis[maxn];
for(int i = 2; i <= N; i++) if(!vis[i])
for(int j = i + i; j <= N; j += i) vis[j] = 1;
最終vis陣列中值為0的即為素數。
應用:給定n,m,求區間[n,m]中的質數個數。
。
首先我們會發現,如果區間中某個數不是質數,那麼它必然有一個小於1E6的質因子。於是我們可以把[1,1E6]範圍內的質數全部篩出來,然後用這些質數去篩區間[n,m]中的數字,複雜度應該是
。程式碼就不放了。
線性篩(尤拉篩法)
顧名思義,它可以線上性時間內篩出區間內質數及積性函式的值。但是在n小於1E6的時候甚至比埃氏篩法慢(因為常數大),於是它的主要功能就變成了求積性函式的值。
考慮如何做到線性。如果我們把每個數字都用它的最小質因子篩去的話就行了,但是怎麼做呢?
從小到大列舉每個數字,再列舉當前已經篩出來的質數,篩掉數字乘質數的值。如果數字是當前質數的倍數就可以退出了,因為質數繼續列舉就不是最小質因子了。因此此時效率為
。程式碼如下:
const int maxn = 100005, N = 100000;
int vis[maxn], prime[maxn], cnt;
for(int i = 2; i <= N; i++){
if(!vis[i]) prime[++cnt] = i;
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0) break;
}
}
上面說過了,這個東西還可以篩出積性函式的值。積性函式定義為對於任意互質的數對 。我們上面實際上算出了每個數的最小質因子,當然也可以算出積性函式的值了,當然我們需要計算任意的質數以及單個質數的冪次對應的f的值,因為這樣才可以利用質因數分解計算出任意數字的f。
const int maxn = 100005, N = 100000;
int f[maxn], pw[maxn], vis[maxn], prime[maxn], cnt;
//pw[i]為i最小質因子的次數
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
pw[i] = 1;
f[i] = ...;//要求給出i為質數時f(i)的值
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0){
f[mul] = f[i / pow(p, pw[i])] * f[pow(p, pw[i] + 1)];//要求給出i為質數的整數次冪時f的值
pw[mul] = pw[i] + 1;
break;
} else pw[mul] = pw[i] * pw[p];
}
}
當然了,具體計算的時候可以用一些方法把pow去掉。接下來就來列舉一些積性函式的求值:
尤拉函式
小於等於n且與n互質的數的個數。定理:
因此我們會發現,若p為n的一個質因子,則有 .當p為質數時 。於是就可以愉快地寫篩法了:
const int maxn = 100005, N = 100000;
int vis[maxn], phi[maxn], prime[maxn], cnt;
phi[1] = 1;
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
phi[i] = i - 1;
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0){
phi[mul] = phi[i] * p;
break;
} else phi[mul] = phi[i] * phi[p];
}
}
於是我們就可以在 的時間內處理出[1,n]中所有數字的尤拉函式值了。
莫比烏斯函式
當n含有平方因子時函式值為0,n為質數時函式值為1,於是也可以線性篩了。
const int maxn = 100005, N = 100000;
int vis[maxn], mu[maxn], prime[maxn], cnt;
mu[1] = 1;
for(int i = 2; i <= N; i++){
if(!vis[i]){
prime[++cnt] = i;
mu[i] = -1;
}
for(int j = 1; j <= cnt; j++){
int p = prime[j], mul = p * i;
if(mul > N) break;
vis[mul] = 1;
if(i % p == 0) break;//mul含有平方因子,mu值為0
mu[mul] = -mu[i];//實際上是mu[mul] = mu[i] * mu[p];
}
}
於是我們的線性篩就可以用來做題了!
例題
求出上式的值,其中 。
根據尤拉函式的性質 ,可以化簡上式得到