數論複習

歐幾里得算法

給定 \(a,b\) ,求 \(\gcd(a,b)\)
這是 \(\rm oier\) 熟知的結論: \(\gcd(a,b) = \gcd(b, a \% b)\)
因爲假設 \(a=kb+r\) ,且 \(\gcd(a,b)=g\) ,則 \(g|a,g|b\) ,則 \(r=a-kb,g|r\)
給等式兩邊同時除以 \(g\) ,得到 \(\frac{r}{g} = \frac{a}{g}-k\frac{b}{g}\)
\(a'=\frac{a}{g},b'=\frac{b}{g},r'=\frac{r}{g}\) ,因爲 \(\gcd(a,b)=g\) ,所以 \(a' \perp b'\)\(\perp\) 表示互質)。
假設 \(b',r'\) 不互質,它們有一個公因數 \(d\) ,則 \(kb'+r'=a'\)\(a'\) 也應該有 \(d\) 這個因數,那麼 \(a',b'\) 不互質,矛盾。因此 \(b',r'\) 互質, \(\gcd(b,r)=\gcd(a,b)\)

int gcd(int a, int b){
    if(!b)return a;
    return gcd(b, a%b);
}

擴展歐幾里得算法

給定 \(a,b,c\) ,求方程 \(ax+by=c\) 的一組解。
\(a'=b,b'=a \% b\) ,假設求出了方程 \(a'x_0+b'y_0=c\) 的一組解 \((x_0,y_0)\) ,考慮怎麼求原方程的解。
顯然有 \(bx_0 + (a-b\lfloor \frac{a}{b} \rfloor)y_0 = c\) ,移項得 \(ay_0 + b(x_0-b\lfloor \frac{a}{b} \rfloor y_0)=c\)
因此我們能寫出一組解 \(x=y_0,y=(x_0-b\lfloor \frac{a}{b} \rfloor y_0)\)
因爲歐幾里得算法的遞歸底層是形如 \(ax=c\) 的方程,而此時 \(a\) 就是最大公約數。
所以只有當 \(\gcd(a,b)|c\) 時方程纔有解,否則無解。
而最後我們能夠得到一組特解 \((x',y')\) ,我們希望得到通解的公式。具體來說就是形如 \((x'+k\Delta_1, y'-k\Delta_2)\) 的公式。其中 \(\Delta_1 \perp \Delta_2\) ,否則我們將它們同時除以它們的 \(\gcd\) 能夠得到一對更小的 \((\Delta_1,\Delta_2)\) (我們當然是想要最小化 \((\Delta_1,\Delta_2)\))。
\(\gcd(a,b) = g\)\(a(x'+k\Delta_1)+b(y'-k\Delta_2)=c\) , 則 \(ak\Delta_1=bk\Delta_2,\frac{a}{b}=\frac{\Delta_2}{\Delta_1}\) ,左邊可以約分成 \(\frac{\frac{a}{g}}{\frac{b}{g}}\) 。那麼 \(\Delta_1 = \frac{b}{g},\Delta_2 = \frac{a}{b}\)

void exgcd(int a, int b, int c, int &x, int &y){
    if(!b)return x = c/a, y = 0, void();
    int x0, y0;
    exgcd(b, a%b, c, x0, y0), x = y0, y = (x0-b*(a/b)*y0);
}

中國剩餘定理(\(\rm CRT\)

給定 \(a_1,a_2\cdots a_m, p_1, p_2, \cdots p_m\) ,其中 \(p\) 兩兩互質,求解方程組

\[\left \{ \begin{array}{lcl} x \equiv a_1 \pmod{p_1} \\ x \equiv a_2 \pmod{p_2} \\ \cdots\\ x \equiv a_m \pmod{p_m} \end{array}\right. \]

構造 \(T = \prod_{i=1}^m p_i, P_i = \frac{T}{p_i}, q_i \equiv P_i^{-1} \pmod{p_i}\) ,則有特解:
\(x = \sum_{i=1}^ma_iP_iq_i\)
考慮這個特解去和 \(p_j\) 取模,當 \(i=j\) 時剛好等於 \(a_j\) ,當 \(i \neq j\) 時由於 \(P_i\) 中包含 \(p_j\) 這一項,所以爲 \(0\) 。因此這個特解滿足上面所有同餘方程。
現在構造通解。同樣地令 \(x'\) 表示之前的特解,令 \(x=x'+k\Delta\)
那麼顯然 \(\forall i, p_i|\Delta\) 。由於 \(p_i\) 兩兩互質,所以 \(\Delta\) 應是 \(T\)

int crt(int x = 0){
    T = 1;
    for(int i = 1; i <= m; ++i)T *= p[i];
    for(int i = 1; i <= m; ++i)P[i] = T/p[i], q[i] = fpow(P[i], mod-2);
    for(int i = 1; i <= m; ++i)x += a[i]*P[i]*q[i];
    return x;
}

擴展中國剩餘定理(\(\rm exCRT\)

給定 \(a_1,a_2\cdots a_m, p_1,p_2,\cdots p_m\) ,不滿足 \(p\) 兩兩互質,求解方程組

\[\left \{ \begin{array}{lcl} x \equiv a_1 \pmod{p_1} \\ x \equiv a_2 \pmod{p_2} \\ \cdots\\ x \equiv a_m \pmod{p_m} \end{array}\right. \]

先考慮 \(m=2\) 的情況,此時有 \(x \equiv a_1 \pmod{p_1}, x\equiv a_2 \pmod{p_2}\)
假設 \(x\) 最後能表示成 \(a_1+k_1p_1\) 的形式,同樣地設 \(x=a_2+k_2p_2\)
那麼 \(a_1+k_1p_1 = a_2+k_2p_2\) ,移項得 \(k_1p_1-k_2p_2 = a_2-a_1\)
因此我們能夠用 \(\rm exgcd\) 解出一組特解 \(b_1,b_2\) ,即 \(b_1p_1-b_2p_2 = a_2-a_1\)
根據 \(\rm exgcd\) 的知識,我們可以構造 \(k_1,k_2\) 的通解,即 \(k_1=b_1+k\frac{p_2}{\gcd(p_1,p_2)},k_2=b_2-k\frac{p_1}{\gcd(p_1,p_2)}\)
那麼有 \(x = a_1+p_1(b_1+k\frac{b}{\gcd(a,b)}) = a_1+p_1b_1+k\rm{lcm}(p_1,p_2)\) ,也就是 \(x \equiv a_1+p_1b_1 \pmod{\rm{lcm}(p_1,p_2)}\)
同樣地我們可以寫成 \(x \equiv a_2+p_2b_2 \pmod{\rm{lcm}(p_1,p_2)}\) 的形式。但由於 \(a_1+p_1b_1 = a_2+p_2b_2\) ,所以這兩個方程其實是一樣的。因此我們將兩個方程合併成一個方程了。
所以對於 \(m>2\) 的情況,就挨着合併方程即可。

int excrt(){
    int lstp = p[1], lsta = a[1];
    for(int i = 2; i <= m; ++i){
        int x, y;
        exgcd(lstp, p[i], lsta, a[i], x, y);
        lsta = lsta+lstp*x, lstp = lstp*p[i]/gcd(lstp, p[i]), lsta %= lstp;
    }
    return lsta;
}

盧卡斯定理(\(\rm lucas\)

\({n\choose m} \equiv {\lfloor \frac{n}{p} \rfloor \choose \lfloor \frac{m}{p} \rfloor}{n\%p \choose m\%p} \pmod p\) ,其中 \(p\) 是質數。
引理\((a+b)^p \equiv a^p+b^p \pmod p\)
顯然。 用二項式定理展開得 \(\sum_{i=0}^p{p \choose i}a^{i}b^{p-i}\)
\(0<i<p\)時,因爲 \(p\) 是質數,所以 \({p \choose i} \equiv 0 \pmod p\)
而當 \(i=0,p\) 時, \({p\choose i} \equiv 1 \pmod p\)
所以 \((a+b)^p \equiv a^p+b^p \pmod p\)
定理證明\((1+x)^n \equiv (1+x)^{p\lfloor \frac{n}{p} \rfloor}(1+x)^{n \% p} \equiv (1+x^p)^{\lfloor \frac{n}{p} \rfloor}(1+x)^{n\% p} \pmod p\)
考慮 \(x^m\) 的係數,將 \((1+x)^n\) 二項式定理展開可以得到係數爲 \(n \choose m\)
而將右邊的式子展開, \((1+x^p)^{\lfloor \frac{n}{p} \rfloor}\)\(x^{p\lfloor \frac{m}{p} \rfloor}\) 次項係數爲 \(\lfloor \frac{n}{p} \rfloor \choose \lfloor \frac{m}{p} \rfloor\) ,而 \((1+x)^{n\% p}\)\(x^{m \% p}\) 次項係數爲 \(n\%p \choose m\%p\) 。所以 \({n\choose m} \equiv {\lfloor \frac{n}{p} \rfloor \choose \lfloor \frac{m}{p} \rfloor}{n\%p \choose m\%p} \pmod p\)
這個定理告訴我們,如果把 \(n,m\) 都寫成 \(p\) 進制的形式,如果存在某一位使得 \(m\) 的這一位 \(>n\) 的這一位,則組合數模 \(p\)\(0\)

int lucas(int n, int m, int p){
    if(m < n)return 0;
    if(n < p && m < p)return C(n, m);
    return lucas(n/p,m/p,p)*C(n%p,m%p)%p;
}

擴展盧卡斯 \(\rm exLucas\)

給定 \(n,m,p\)\({n \choose m}\pmod p\)\(p\) 不一定爲質數。(\(2 \leq p \leq 10^6\)
首先根據唯一分解定理把 \(p\) 表示成 \(\prod_{i=1}^{cnt} p_i^{k_i}\) 的形式。
假設我們對於所有 \(i\) 求出了 \(a_i \equiv {n\choose m} \pmod{p_1^{k_1}}\) ,那麼只需要解方程組

\[\left \{ \begin{array}{lcl} x \equiv a_1 \pmod{p_1^{k_1}} \\ x \equiv a_2 \pmod{p_2^{k_2}} \\ \cdots\\ x \equiv a_{cnt} \pmod{p_{cnt}^{k_{cnt}}} \end{array}\right. \]

即可。因爲 \(p_i^{k_i}\) 之間肯定是互質的,最後的 \(x\) 一定模 \(p\) 同餘 \(n\choose m\)
現在的問題是怎麼求解 \({n\choose m}\pmod{p^k}\)
考慮快速計算階乘。注意到這裏是不能隨便取模的,因爲組合數上下都可能包含與 \(p^k\) 不互質的數,這一部分應該上下約分後再計算,其它部分可以直接算。因爲這裏的 \(p\) 是質數,所以與 \(p^k\) 不互質的數一定是 \(p\) 的倍數。
先計算階乘中與 \(p^k\) 互質的部分。可以發現 \(1,2,\cdots p^k\) 中與 \(p^k\) 互質部分的乘積與 \(p^k+1,p^k+2,\cdots 2p^k\) 是相等的。所以我們可以 \(\mathcal{O}(p^k)\) 計算出每一段的互質部分,再計算 \([n-n\%p +1,n]\) 的互質部分。
接下來我們需要計算 \(p\times 2p\times \cdots \lfloor \frac{n}{p} \rfloor p\)。只需要遞歸計算 \(\lfloor \frac{n}{p} \rfloor!\) 即可。
最後需要計算 \(n!\)\(p\) 的指數。我們可以先計算 \(n\) 以內 \(p\) 的倍數個數,這些數都會給最後 \(p\) 的指數貢獻 \(1\) ;再計算 \(p^2\) 的倍數……就能算出最後 \(p\) 的指數了。
使用這種快速階乘的算法計算組合數的三個階乘即可。複雜度 \(\mathcal{O}(p \log_{p}n)\)

int inv(int a, int p){
    int x, y;
    return exgcd(a, p, 1, x, y), (x%p+p)%p;
}
int fac(int n, int p, int pk, int ans = 1){
    if(!n)return 1;
    for(int i = 1; i <= pk; ++i)if(i%p > 0)ans = ans*i%pk;
    ans = fpow(ans, n/pk, pk);
    for(int i = 1; i <= n%pk; ++i)if(i%p > 0)ans = ans*i%pk;
    return ans*fac(n/p, p, pk)%pk;
}
int C(int n, int m, int p, int pk){
    if(n < m)return 0;
    int fac1 = fac(n, p, pk), fac2 = fac(m, p, pk), fac3 = fac(n-m, p, pk), cnt = 0;
    for(int i = p; i <= n; i *= p)cnt += n/i;
    for(int i = p; i <= m; i *= p)cnt -= m/i;
    for(int i = p; i <= n-m; i *= p)cnt -= (n-m)/i;
    return fac1*inv(fac2, pk)*inv(fac3, pk)*fpow(p, cnt, pk)%pk;
}
int exlucas(){
    for(int i = 2; i*i <= P; ++i)if(P%i == 0){
        int pk = 1;
        while(P%i == 0)P /= i, pk *= i;
        p[++cnt] = pk, a[cnt] = C(n, m, i, pk);
    }
    if(P > 1)p[++cnt] = P, a[cnt] = C(n, m, P);
    return crt();
}

大步小步算法\(\rm BSGS\)

給定 \(a,n,p\) ,求解 \(a^x \equiv n \pmod p\)\(a,\perp p\)
\(x = iB+j(j<B)\) ,我們對於所有 \(j\) 可以預處理出 \(a^j\) 的值。
然後我們枚舉 \(i\) ,查詢是否存在一個 \(j\) ,使得 \(a^{iB+j} \equiv n \pmod p\) ,即 \(a^j \equiv na^{-iB} \pmod p\) 。用 \(\rm STL::map\) 可以實現。
那麼複雜度就是 \(B\log B+\lfloor \frac{P}{B} \rfloor \log B\) ,發現當 \(B=\sqrt{p}\) 時最優,複雜度爲 \(\mathcal{O}(\sqrt{q}\log \sqrt{q})\)

int bsgs(){
    map<int, int> tab;
    int B = sqrt(q), mul = 1, aB = fpow(a, B), qry;
    for(int i = 0; i < B; ++i)tab[mul] = i,, mul = mul*a%p;
    mul = 1;
    for(int i = 0; i < p/B; ++i){
        qry = n*inv(mul)%p;
        if(tab.count(qry))return i*B+tab[qry];
        mul = mul*aB%p;
    }
    return -1;
}

擴展大步小步算法\(\rm exBSGS\)

給定 \(a,n,p\) ,求解 \(a^x \equiv n \pmod p\)
首先當 \(a \perp p\) 時,可以直接用 \(\rm BSGS\) 求解。
否則把 \(a^{x-1}\) 看作未知數,設 \(\gcd(a,p)=d\) ,如果 \(d \nmid n\) 則無解。
於是同時除以 \(d\) 得到 \(\frac{a}{d}a^{x-1} \equiv \frac{n}{d} \pmod{\frac{p}{d}}\)
若此時仍然 \(a \nmid \frac{p}{d}\) ,則繼續求 \(\gcd\) 再除下去,最後得到的形式就是 \(\frac{a}{d_1d_2\cdots d_k} \equiv \frac{n}{d_1d_2\cdots d_k} \pmod{\frac{p}{d_1d_2\cdots d_k}}\)
\(D = \prod_{i=1}^kd_i\)
此時 \(\frac{a}{D} \perp \frac{p}{D}\) 。這時就可以直接解 \(a^{x-k} \equiv \frac{n}{D}(\frac{a^k}{D})^{-1} \pmod{\frac{p}{D}}\)
注意如果除的過程中 \(\frac{a^i}{d_1d_2\cdots d_i} \equiv \frac{b}{d_1d_2\cdots d_i}\) ,則說明 \(x<k\) ,此時直接 \(x=i\) 即可。

int exbsgs(){
    int d, k = 0, a2 = 1, ans;
    while((d = gcd(a, p)) > 1){
        if(n%d > 0)return -1;
        ++k, a2 *= a/d, n /= d, p /= d;
        if(a2 == n)return k;
    }
    n = n*inv(a2, p), ans = bsgs(a, n, p);
    return ans == -1 ? -1 : ans+k;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章