1. 程式人生 > 其它 >2021 SDSC D1 基礎數論

2021 SDSC D1 基礎數論

Part 1 初月(Easy Mode)

質數

暴力

\(2\) 列舉到 \(⌊\sqrt n⌋\) 試試是否可以整除就可以啦。

顯然時間複雜度是 \(O(\sqrt n)\) 的。

埃氏篩

剛才的試除法讓我們知道,找到一個數的因數是很難的。

但是找到一個數的倍數就很簡單了。

因此我們可以從 \([2,n]\) 中依次列舉,每個數的倍數必然不是質數
複雜度?

\(F(n)=\frac{n}{2}+\frac{n}{3}+⋯+\frac{n}{n}=O(n \log n)\)

實際上底數是 \(e\),因此調和級數的常數很小,\(1s\) 跑得過。

由算術基本定理可知,只需要列舉質數的倍數即可。

列舉 \(p\) 的倍數時,只需從 \(p^2\) 開始列舉。

時間複雜度 \(O(n \log \log n)\)

線性篩

剛才的做法已經很優秀了,可是為什麼還不是線性呢?

原因仍然在於,一個數字可能被篩掉多次。

例如 \(n=15\),那麼\(12\) 這個數會在 \(p=2\)\(p=3\) 時各被篩一次。

想要做到線性,就必須讓一個數只被一個質數篩掉,我們選擇這個數為它最小的質因數。

首先從 \(2\)\(n\) 列舉自然數 \(q\)(不一定是質數),再從小到大列舉比 \(q\) 小的質數 \(p\),篩掉 \(pq\);如果 \(q\)\(p\) 的倍數,就跳出內層迴圈。

void primes(int n)
{
    memset(v, 0, sizeof(v)); //清空標記陣列
    m = 0;                   //質數個數
    for (int i = 2; i <= n; i++)
    {
        if (!v[i])                    //未被標記,i為質數
            v[i] = i, prime[++m] = i; //記錄
        for (int j = 1; j <= m; j++)
        {
            if (prime[j] > v[i] || prime[j] > n / i)
                break;                  //i有更小的質因子,或者超出n的範圍
            v[i * prime[j]] = prime[j]; //prime[j]為合數 i*prime[j]的最小質因子
        }
    }
}

根據“高斯素數定理”,如果需要粗略的估計 \([2,n]\) 內的素數個數,可以直接硬點它為 \(\frac{n}{\ln n}\)

GCD & LCM

GCD,Greatest Common Divisor 的縮寫,意為最大公約數

LCM,Least Common Multiple 的縮寫,意為最小公倍數

\(\gcd(a,b)=\gcd(a−b,b)= \gcd(b,a\text{ mod } b)\)

注意到每次取模至少減半,因此複雜度為 \(O(\log n)\)

而求 LCM 只需求出 GCD ,因為:

\(\gcd(a,b) \times \text{lcm}(a,b)=a \times b\)

進位制轉換

\(n\)\(a\) 進位制數轉化為 \(b\) 進位制數,最終為 \(m\) 位。

將給定的 \(a\) 進位制數從高位向低位掃描,每次將當前結果乘以 \(a\),並加上當前位的係數,再從低位向高位進位即可。

複雜度 \(O(nm)\),用多項式科技可以優化,但大多數情況下沒必要。

瓶頸在於 \(n\) 次乘 \(a\) 和進位,每次都是 \(O(m)\) 的。

如果 \(a, b\) 比較小,可以通過壓位的方式進一步優化。

即,先將原數轉化為 \(a^p\) 進位制,再轉化為 \(b^q\) 進位制,可以將複雜度降為 \(O(\frac{nm}{pq})\),為了計算方便,以 \(a^p,b^q\) 不超過 \(2×10^9\) 為妙。

Part 2 三日月(Normal Mode)

同餘

若對於給定的正整數 \(m\),有正整數 \(a,b\),滿足 \(a=km+b,k∈Z\),則稱 \(a,b\)\(m\) 同餘,用 \(a≡b(\text{mod}m)\) 表示。

\(m\) 意義下,整數集只剩下了 \([0,m)\)\(m\) 個整數,其他整數都應該加上或減去若干個 \(m\) 來調整到這個區間內。

注意到負整數 \(x\) 對應的數是 \(km+x\),而不是 \(km−x\)

順便提一句,\(a\text{mod}b=a−⌊\frac ab⌋ \times b\),這一點後面還會提到。

不難證明,模意義下的加法、減法、乘法與一般意義下相同,各運算規律依然滿足。

但是很可惜,除法與它們不同。

考慮一般意義下的除法,\(a÷b\) 等價於 \(a \cdot b^{-1}\),而 \(b^{−1}\) 滿足 \(b\cdot b^{−1}=1\) 這一性質,從而進行乘法的逆運算。

換句話說,假如我們能對任意 \(b\) 找到 \(b^{−1}\),使得\(b\cdot b^{−1}≡1(\text{mod}m)\),不就能實現模 \(m\) 意義下的除法了嗎?

逆元

因此,我們需要引入逆元的概念。

大多數情況下,當我們需要在模意義下做除法時,模數都是質數。

此時 \((0,m)\) 中每個整數都有唯一的逆元。

費馬小定理

若整數 \(a∈(0,m)\)\(m\) 為質數,則 \(a^{m−1}≡1(\text{mod}m)\)

有了它,我們就能求出 \(a\) 的逆元了:

\(a\cdot a^{m−2}=a^{m−1}≡1(\text{mod}m)\),所以 \(a^{−1}≡a^{m−2}(\text{mod}m)\)

絕大多數情況下逆元都是這樣求的。

擴充套件歐幾里得演算法 & 裴蜀定理

\(x=a^{−1}\),那麼有 \(a\cdot x≡1(\text{mod}m)\)

將它寫成一般形式,得到 \(x,y\) 的不定方程 \(ax−my=1\)

如果能得到一組整數解 \((x_0,y_0)\),我們就可以得到它的通解:

\(x=x_0+km,y=y_0+ka,k∈Z\),選擇合適的 \(k\) 即可得到 \(x\)

現在問題在於,方程是否有解,以及如何找到一組特解。

必要性很好證明,而下面要講的擴充套件歐幾里得演算法則通過構造證明了充分性。

\(d=\gcd(a,b)\),擴充套件歐幾里得演算法可以給出不定方程 \(ax+by=d\) 的一組整數特解 \((x_0,y_0)\)

類比剛才的情況,通解有 \(x=x_0+k\cdot \frac bd,y=y_0−k\cdot \frac ad,k∈Z\)

對於 \(ax+by=c,d|c\) 的情況,可以先解 \(ax+by=d\),再將 \(x,y\) 都乘以 \(\frac cd\)

考慮之前歐幾里得演算法求 \(\gcd\) 的過程,我們從 \(\gcd(a,b)\)遞迴到 \(\gcd(b,a\text{mod}b)\)

\(a^′=b,b^′=a\text{mod}b\),那麼如果我們有了不定方程 \(a^′x+b^′y=d\) 的一組整數解 \((x^′,y^′)\),能否推出 \(ax+by=d\) 的一組整數解 \((x,y)\) 呢?

\(b^′=a\text{mod}b=a−⌊a/b⌋ \cdot b\),代回 \(a^′x^′+b^′y^′=d\) 得到 \(bx^′+(a−⌊a/b⌋\cdot b)y^′=d\),整理得$ ay′+b(x′−⌊a/b⌋\cdot y^′)=d$。

也就是說,令 \(x=y^′,y=x^′−⌊a/b⌋\cdot y^′\) 即可完成遞迴的回推

裴蜀定理:對於給定的正整數 \(a,b\),設 \(d=\gcd(a,b)\),則關於 \(x,y\) 的不定方程 \(ax+by=c\) 存在整數解,當且僅當 \(d|c\)

尤拉函式

尤拉函式:定義域為正整數的函式 \(φ(x)\),表示 \([1,x]\) 中與 \(x\) 互質的正整數個數。

\(x=∏p_i^{e_i}\),則 \(φ(x)=x\cdot ∏\frac{p_i−1}{p_i}\),證明可以考慮每次劃掉 \(x\) 所有質因子的倍數,剩下的就是與 \(x\) 互質的。

尤拉函式可以線性篩,只需判斷當前質因子是否出現過,以選擇乘以 \(p\) 還是 \(p−1\)

尤拉定理

那麼為什麼要講尤拉函式呢?就是為了引出尤拉定理!

\(a,m\) 互質,則 \(a^{φ(m)}≡1(\text{mod}m)\)

\([1,m]\) 中與 \(m\) 互質的數列出來,設為 \(b_1,b_2,⋯,b_{φ(m)}\)

\(c_i=a\cdot b_i\text{mod }m\),可以證明 \(c_i\) 兩兩模 \(m\) 不同餘,且任意 \(c_i\)\(m\) 互質。

那麼 \(c\) 其實是 \(b\) 重排的結果,因為與 \(m\) 互質的數只有 \(φ(m)\) 個。

因此 \(∏ b_i≡∏ c_i≡a^{φ(m)}\cdot ∏ b_i(\text{mod}m)\),得證。

前面提到的費馬小定理其實是尤拉定理的子定理。

擴充套件尤拉定理

剛才的尤拉定理只適用於 \(a,m\) 互質的情況

對於不互質的情況,此處不證明地給出擴充套件尤拉定理:

\(c<φ(m),則a^c≡a^c(\text{mod}m)\)

\(c≥φ(m),則 a^c≡a^{[cmodφ(m)]+φ(m)}(\text{mod}m)\)

證明比較複雜,有興趣的同學可以自行查閱或瀏覽這篇部落格

Part 3 上弦月(Hard Mode)

中國剩餘定理

BSGS 演算法

整除分塊(數論分塊)

積性函式

莫比烏斯反演