昨天的考試跪的一塌糊塗:第一題水過,第二題帶wa的樸素,最後題忘了特判左端點全跪,分數比起預計得分整整打了個對折啊!
步入正題:線性篩(尤拉篩)
一般的篩法(ppt裡叫埃拉託斯特尼篩法,名字異常高貴)的效率是o(nlglgn)(其實很接近o(n)啊!),對於一些例如n=10000000的殘暴資料會跪,於是,線性篩登場了…
1 #include 2 using namespace std;以上是線性篩**。3 int prime[1100000],primesize,phi[11000000];
4 bool isprime[11000000];
5 void getlist(int listsize)
6 17 }
18 }
就我的理解,線性篩有兩個地方與一般篩不同:
1.兩層迴圈的順序不同(一般篩是第一維prime[i] 第二維j,尤拉篩是第一維i 第二位prime[j])
2.一行神奇的**:
14 if(i%prime[j]==0)break;這行**神奇地保證了每個合數只會被它的最小素因子篩掉,就把複雜度降到了o(n)。
接下來是證明這個演算法正確性的說明:
prime陣列中的素數是遞增的,當i能整除prime[j],那麼i*prime[j+1]這個合數肯定被prime[j]乘以某個數篩掉。之前看了網上各種各樣的說法,整個理解都有問題,總算是明白了。因為i中含有prime[j],prime[j]比prime[j+1]小,即i=k*prime[j],那麼i*prime[j+1]=(k*prime[j])*prime
[j+1]=k』*prime[j],接下去的素數同理。所以不用篩下去了。因此,在滿足i%prime[j]==0這個條件之前以及第一次
滿足改條件時,prime[j]必定是prime[j]*i的最小因子。
顯然,線性篩只拿來篩篩素數是很不科學的,它的速度大約是一般篩的3~4倍,在資料量小的時候甚至慢些(用到了mod運算)。
接下來就是它的重量級應用了:求解積性函式
p.s.睡了一覺,精神抖擻!
積性函式f(x)應滿足f(a×b)=f(a)×f(b),a與b應互素。而完全積性函式則應滿足對於任意的a與b,前面的等式應成立。
先是尤拉phi函式(反演不怎麼懂):
1 void getlist(int listsize)至於為什麼這樣的遞推是成立的,有如下的幾個原因:2 12 for(int j=1;j<=primesize&&i*prime[j]<=listsize;j++)
13
20 phi[i*prime[j]]=phi[i]*(prime[j]-1);
21 }
22 }
23 }
1.每個合數只會被篩到一次(前面說明過)。
2.當正整數p是素數時,phi[p]=p-1。
3.phi函式是乙個積性函式,當a與b互素時,滿足phi(a×b)=phi(a)×phi(b)。
4.上述**中的i永遠大於等於prime[j],又因為prime[j]必然是素數,所以i、prime[j]互素當且僅當i=0 (mod prime[j])。
5.當p是素數時,phi(pk)=(p-1)×pk-1
原因1保證了每個數的phi只會被計算一次,第10行的**可以由原因2解釋,第20行的**可以由原因2與原因3解釋,第17行的**可以由原因5所解釋。
還是覺得這個演算法好神奇……
接下來的東西就和昨天的考試有關了,先是求每個數的因數個數:
記每個數因數個數為e(i),顯然函式e(i)是積性函式但不是完全積性函式。
首先,當p是素數時e(i)=2。
對於i與prime[j]互素的情況,直接相乘即可。而對於i與prime[j]不互素的情況,就有點複雜了。
我們要再記錄乙個num陣列,num[i]表示i的最小素因子(即每次遞推時的prime[j])的次數。
對於i與prime[j]不互素的情況,e(i×prime[j])=e(i)÷(num[i]+1)×(num[i]+2)。
num陣列的維護也是很方便的:對於所有n為素數或n=i×prime[j](i與prime[j]互素)的情況,num(i)=1,對於另外一種情況num[i*prime[j]]=num[i]+1。
再是求因數和還有因數平方和(兩者差不了多少)
設數n的因數和為q(n),有q(n)=(1+p1+p1
2+…+p1
k1)×(1+p2+p2
2+…+p2
k2)×…×(1+pn+pn
2+…+pn
kn)。
顯然,函式q是積性的,我們只用考慮i與prime[j]不互質的情況,一種辦法是用乘法逆元搞,顯然這是不科學的,真正的做法如下
q(i×prime[j])=q(i÷prime[j]num[i])×q(prime[j]num[i]+1)
其中的prime[j]num[i]是可以預先存好的。這種做法還是利用了積性函式的性質。
最後附上昨天第二題的**
1 #include 2 #include 3 using namespace std;4 inline long long imax(long long a,long long b)
5 const int mo=1000000007;
6 long long q[2100000];
7 long long prime[11000000],pnum,ynum[11000000],ysum[11000000],c[11000000];
8 bool isprime[10000001];
9 void getlist(int listsize)
10 22 for(int j=1;j<=pnum&&i*prime[j]<=listsize;j++)
23
34 else
35
39 break;
40 }
41 else
42
47 }
48 }
49 }
50 int main(int argc, char *argv)
51 62 getlist(biggest);
63 long long ansnum=0,anssum=0;
64 for(int i=1;i<=n;i++)
65
73 }
74 cout<
尤拉篩 線性篩
實現 include using namespace std const int max n 1e8 int prime max n cnt bool st max n 使用bool陣列節省空間 void is prime int n intmain 每個合數只被自己最小的質因子篩去。現在證明在i ...
尤拉篩(線性篩)
尤拉篩多用於篩素數,時間複雜度是o n 主要原理 合數 最小質因子 合數 質數 這個合數的組成是唯一的,尤拉篩裡面只在這種情況篩一次,也就是每個數就篩一次,可以完成o n 的複雜度。每當列舉到乙個數,把這個數當作後面的那個數,在已經得到的質數里列舉當作最小質因子,看看這樣的組合能找到哪個合數,但是要...
線性篩法(尤拉篩)
從前有乙個素數篩法叫埃拉託斯特尼篩法,它的思想很簡單,把1 n以內素數的整數倍的數字劃掉,留下的就全是素數,但是它的複雜度是o nlglgn 對於大量不友好資料會跪,於是線性曬登場了。include using namespace std int prime 1100000 primesize,ph...