線性篩與莫比烏斯反演
線性篩與莫比烏斯反演
和上篇文章一樣,一直沒有研究這個東西,結果又考了GG……TAT
下定決心學一學,搞好這個東西。
線性篩
篩質數有很多方法,好像很厲害的有洲閣篩、杜教篩(
然而我都不會QAQ),比較坑的有暴力篩(就是枚舉一個數的倍數)。
我只學了比較簡單而且實用的線性篩法。
這種篩法是避免一個數被重復篩幾遍,所以效率均攤下來可以達到線性。(網上有證明)
先上代碼:
const int N = 100000;
bool is_prime[N+100];
int prime[N], cnt = 0;
void find_prime() {
Set(is_prime, true);
is_prime[0 ] = is_prime[1] = false;
For(i, 2, N) {
if (is_prime[i])
prime[++cnt] = i;
For(j, 1, cnt) {
if (i * prime[j] > N) break;
is_prime[i * prime[j] ] = false;
if (i % prime[j] == 0) break; //here
}
}
}
這個代碼有一個關鍵點 就是上面的\(here\)
對於一個合數\(m\)可以分解為\(m=p_1^{r_1}*...*p_n^{r_n}\)其中
\(p_i\)為質數,那麽我們篩\(m\)的時候之前把\(p_1\)篩掉了,所以在
枚舉\(i\)的時候。
如果\(i\)為素數沒問題,直接向後繼續推(因為篩出的
質數都類似\(m=p_1*p_2\)的形式,所以不可能重復)。如果為合數,那麽\(i\)可以分解成\(i=p_1^{r_1}*...*p_n^{r_n}\)形式
其中\(p_1-p_n\)是遞增的,那麽\(p_1\)是最小的那個質數。\(i \bmod p_1 = 0\)的時候,就不用繼續枚舉了,
所以我們就只能篩出不大於\(p_1\)
莫比烏斯反演
莫比烏斯反演很多時候都能大大簡化運算……
- 定理:\(F(n)\)和\(f(n)\)是定義在非負整數集合上的兩個
函數,並且滿足條件\(F(n)=\sum \limits \limits _{d|n}{f(d)}\)。那麽我們
就能得到結論:
\(f(n)=\sum \limits \limits _{d|n}\mu(d)F(\frac{n}{d})\)
在上面的公式中有一個\(\mu(d)\)函數,它的定義如下:
(1)若\(d=1\),那麽\(\mu(d)=1\)
(2)若\(d=p_1p_2...p_k\),\(p_i\)均為互異質數,
那麽\(\mu(d)=(-1)^{k}\)。這個我的理解就是\(d\)的
質因數個數為偶數的話,那麽\(\mu(d)=1\)否則為\(-1\)
(3)其他情況下\(\mu(d)=0\)這個就是對上面那條的拓展了,
就是指的\(d\)沒有一個平方因子,或者說沒有一個質因子的
次數大於\(1\)
線性篩求莫比烏斯函數的代碼:
const int N = 100100;
bool is_prime[N+100];
int mu[N+100] = {0, 1}, cnt = 0, prime[N+100];
void init() {
Set(is_prime, true);
is_prime[1] = false;
For (i, 2, N) {
if (is_prime[i]) {
prime[++cnt] = i;
mu[i] = -1; //質數的質因子個數肯定為奇數個就是1
}
For (j, 1, cnt) {
if (i * prime[j] > N) break;
is_prime[i * prime[j] ] = false;
if (i % prime[j]) mu[i * prime[j] ] = -mu[i]; //多了一個質因子直接變為原來結果的相反數
else {
mu[i * prime[j]] = 0; //這個將要被篩的數至少具有兩個prime[j]的因子
break;
}
}
}
}
有了上面的知識,現在,我們來證明莫比烏斯反演定理。
證明:
\(\sum \limits _{d|n}\mu(d)F(\frac{n}{d})=\sum \limits_{d|n}\mu(d)\sum \limits_{d‘|\frac{n}{d}}f(d‘) =\sum \limits \limits _{d‘|n}f(d‘)\sum \limits_{d|\frac{n}{d‘}}\mu(d)=f(n)\)
Q.E.D
- 然後還要提一下的就是一些常見的定理,證明嘛……
- 一般都是先分解質因數,然後再根據組合數性質去算,比如第一個。
- 要麽就是對於一些常見的反演格式進行反演,比如第二個。
- \(\sum \limits _{d|n} \mu(d)=[n=1]\)
- \(\sum \limits _{d|n} \frac{\mu(d)}{d}=\frac{\varphi(n)}{n}\)
一些例題(難題)
Luogu 【P1829】[國家集訓隊]Crash的數字表格
題意
求 \(\sum \limits _{i=1}^{n} \sum \limits_{i=1}^{m} lcm(i,j) \ (n,m \le 10^7)\)
題解
一個莫比烏斯反演然後化式子。
\(\sum \limits \limits _{i=1}^{n} \sum \limits_{i=1}^{m} lcm(i,j)\)
\(=\sum \limits \limits _{i=1}^{n} \sum \limits_{i=1}^{m} \frac{i\ j}{gcd(i,j)}\)
\(=\sum \limits \limits _{d=1}^{min(n,m)} \ d \sum \limits_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{d} \rfloor} \ ij \ [gcd(i,j)=1]\)
這個就是一個更換枚舉相的操作了,是個套路。
你先枚舉所有可能的\(gcd\)再計算這種\(gcd\)的貢獻。
比如前面的那個\(d\)就是我們枚舉的\(gcd\),後面所有可能的數對,就是在\(\lfloor \frac{n}{d} \rfloor\)和\(\lfloor \frac{m}{d} \rfloor\)中的所有互質的數對的乘積在乘上\(d\)。
這個可以簡單理解一下,就是兩個數分別除以他們的最大公因數,然後兩個數肯定是互質的。
但其對於答案的貢獻就多除以了一個\(d\),所以要乘回來。
\(ans =\sum \limits _{d=1}^{min(n,m)} \ d \sum \limits_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum \limits_{j=1}^{\lfloor \frac{m}{d} \rfloor} \sum \limits_{x|gcd(i,j)}\mu(x) * i * j\)
這個就是運用了前面的公式\(\sum \limits _{d|n} \mu(d)=[n=1]\)來替代了\([gcd(i,j)=1]\)的條件。(這個就是套路了)
然後我們繼續推:
\(ans =\sum \limits \limits _{d=1}^{min(n,m)} d \sum \limits_{x=1}^{min(\lfloor \frac{n}{d} \rfloor,\lfloor \frac{m}{d} \rfloor)} \mu(x) \ x^2 \sum \limits_{i=1}^{\lfloor \frac{n}{dx} \rfloor} i \sum \limits_{j=1}^{\lfloor \frac{m}{dx} \rfloor} j\)
這個也是套路,把\(x\)提前了。就是改成了枚舉\(x\)看看它的對於答案的貢獻是多少。
很容易發現,就是在\([1,\lfloor \frac{n}{dx} \rfloor]\)中的所有數乘上\(x\)
就是原來可行的\(i\)。然後我們就可以根據這個來優化了。
前面那兩個\(\sum \limits\)就是\(n \ ln \ n\)(令\(n=max(n,m)\))的復雜度。(就是\(\sum \limits \limits _{i=1}^{n} \frac{n}{i}\))。後面的那兩個,直接用等差數列求和公式\(O(1)\)算。
但這個仍然過不去...(\(O(1.61*10^8)\),$ % $的常數還很大)所以就需要來用套路的整除分塊了。
就是把後面兩個 \(\sum \limits\) 很多一樣答案的地方一起處理掉,所以對於那個 \(\mu(x) \ x^2\) 還要記一個前綴和。
總復雜度\(O(\sum \limits _{i=1}^{n} \sqrt{\frac{n}{i}})=O(pass)\)這個我也不會算。。大佬說似乎是\(O(n^{\frac{2}{3}})\)。
線性篩與莫比烏斯反演