数论复习

欧几里得算法

给定 \(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-\lfloor \frac{a}{b} \rfloor y_0)=c\)
因此我们能写出一组解 \(x=y_0,y=(x_0-\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}{g}\)

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-(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{p_2}{\gcd(p_1,p_2)}) = 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;
}
posted @ 2021-04-08 16:51  修电缆的建筑工  阅读(115)  评论(0编辑  收藏  举报