数论

todo

  • 威尔逊定理 原根 二次剩余,这些另开文章
  • 积性函数与筛子相关
  • 数论分块

目录

  1. 前置知识与符号定义

  2. 素数筛

  3. 裴蜀定理

  4. (扩展)欧几里得算法

  5. 同余方程

  6. 费马小定理

  7. (扩展)欧拉定理

  8. 模意义下的乘法逆元

  9. (扩展)卢卡斯定理

  10. (扩展)中国剩余定理

0.前置知识与符号定义

在无特殊说明的情况下,所有数字均 \(\in \mathbb{Z}\),且一般 \(\in\mathbb{Z^+}\)

0.0 唯一分解定理

对于任意正整数 \(t\) 有唯一分解:

\[t=p_1^{k_1}p_2^{k_2}p_3^{k_3}\cdots p_{i}^{k_i} \]

其中 \(\forall p\) 是互不相同的素数。

这个定理的意思是:每一个正整数都可以分解成若干不同素数的乘积。

0.1. 素数

对于一个数 \(p\)\(p \ne 0,\pm 1\),且 \(p\) 没有除 \(1\)\(p\) 以外的因数,则认为 \(p\) 是素数,否则认为 \(p\) 是合数。

0.2. 最大公因数

定义 \(\gcd(a,b)\)\(a,b\) 两数的最大公因数,最大公因数是指同时是 \(a,b\) 的因数的数。

设两数分别为 \(a,b\),我们会发现最大公因数的一个等价定义:

由于:

\[a=p_1^{k_1}p_2^{k_2}p_3^{k_3}\cdots p_{i}^{k_i} \\ b=p_1^{k_1'}p_2^{k_2'}p_3^{k_3'}\cdots p_{i}^{k_i'} \]

我们有:

\[\gcd(a,b)=p_1^{\min(k_1,k_1')}p_2^{\min(k_2,k_2')}\cdots p_i^{\min(k_i,k_i')} \]

\(\forall p\) 仍是互不相同的质数。

\(\operatorname{lcm}\) 可以同理定义。

0.3. 整除符号

定义 \(d|a\) 表示 \(a\) 可被 \(d\) 整除。

因为我在给 whk 的同学讲的时候手误把定义敲反了,所以一个等价定义是:

\(d|a\),则 \(a \bmod d = 0\),即 \(a=dq,q\in \mathbb{Z}\)

0.4. 模运算

定义 \(a \bmod b=a-\lfloor \frac{a}{b} \rfloor \times b\)

特殊的,当输出一个数 \(k\)\(p\) 取模的结果时,请使用 cout<<(k%p+p)%p 而非 cout<<k%p,这可能导致一些符号错误。

有以下性质:

\[(a \pm b)\bmod m = (a\bmod m \pm b \bmod m)\bmod m \]

\[a \times b \bmod m= (a\bmod m \times b\bmod m) \bmod m \]

证明显然。

\[\dfrac{a}{b}\neq \dfrac{a\bmod m}{b\bmod m}\bmod m \]

见“模意义下的乘法逆元”

0.5. 同余

\(a\bmod m =b\bmod m\),则称 \(a,b\) 同余,即 \(a \equiv b \pmod m\)

有以下性质:

\[m|(a-b)\Longleftrightarrow a\equiv b \pmod m \]

这是同余的一个重要基本性质。

\[a\equiv b \pmod m 且c\equiv d \pmod m \Longrightarrow a\pm c \equiv b \pm d \pmod m \]

\[a\equiv b \pmod m 且 b \equiv c \pmod m \Longrightarrow a\equiv b \equiv c \pmod p \]

由以上性质,有以下三个推论:

\[a\equiv b \pmod m \Longrightarrow a\pm k \equiv b\pm k \pmod m \]

\[a\equiv b \pmod m \Longrightarrow ak\equiv bk \pmod m \]

\[a\equiv b \pmod m \Longrightarrow a^n\equiv b^n \pmod m \]

值得注意的是,等式的基本性质中加减乘三个运算一般在同余中成立,而除法需要特殊讨论,如下式。

\(\gcd (c,m)=1\),则:

\[ac\equiv bc \pmod m \Longrightarrow a\equiv b \pmod m \]

证明:

\[\begin{aligned} &\because ac \equiv bc \pmod m \\ &\therefore m|(ac-bc) \\ &\Rightarrow m|c(a-b) \\ &\because \gcd(c,m)=1 \\ &\therefore m|(a-b) \\ &\Rightarrow a\equiv b \pmod m \end{aligned} \]

并且,若 \(d=\gcd (a,m)\) 我们有推论:

\[ac\equiv bc \pmod m \Longrightarrow a\equiv b \pmod{\dfrac{m}{d}} \]

证明:

\[\begin{aligned} &\because ac \equiv bc \pmod m \\ &\therefore m |c(a-b) \end{aligned} \]

若有整数 \(k\) 使得 \((a-b)c=mk\),则

\[\begin{aligned} & \because m |c(a-b) \\ & \therefore \dfrac{(a-b)c}{d}=\dfrac{mk}{d} \\ & \because \gcd(\frac{c}{d},\frac{m}{d})=1 \\ & \therefore \dfrac{m}{d}|(a-b) \\ & \therefore a \equiv b \pmod {\frac{m}{d}} \end{aligned} \]

这是部分题目中的重要性质。

0.6. 快速幂

和数论没一点关系,放个板子。

https://www.luogu.com.cn/problem/P1226

对于快速幂的问题定义如下:给定两个整数 \(a,b\),求 \(a^b \mod k\)

对于此类问题,暴力时间复杂度 \(O(n)\) ,考虑优化。

我们不妨将指数采用二进制表述,设指数的二进制串为 \(S_{i}\)\(tmp\) 表示当前二进制位对答案的贡献,\(a\) 表示底数。

\(S_{k}=1\),则 \(ans \gets ans \times tmp\)。由于第 \(k\) 位对答案的贡献为\(a^{2^{k-1}} \times a^{2^{k-1}}\),即 \(a^{tmp} \times a^{tmp}\),化简得 \(a^{tmp^2}\) 所以我们在每次操作后使 \(tmp \gets tmp \times tmp\) 即可。

其时间复杂度为 \(O(\log n)\),迭代常数较小。

代码:

int m;
int qpow(int a,int b)
{
    int ans=1,tmp=a;
    while(b!=0)
    {
        if(b&1)
        {
            ans=(ans%m*tmp%m)%m;
        }
        tmp=tmp*tmp%m;
        b>>=1;
    }
    return ans;
}

0.7. 两道奇妙例题

  1. 对于 \(n\in\mathbb{Z}\),证明 \(\dfrac{(n^2)!}{(n!)^{n+1}}\in\mathbb{Z}\)

    组合意义保平安!

    考虑以下问题情景,将 \(n^2\) 个人分成 \(n\) 组,每组 \(n\) 人,求方案总数。

    显然将 \(n^2\) 个人的全排列的方案数为 \((n^2)!\),而在每组内不需要考虑元素的先后顺序,所以多算了 \((n!)^n\) 种方案。而这 \(n\) 组本身也不需要考虑先后顺序,所以又多算了 \(n!\) 种方案。

    所以这个问题的答案即为 \(\dfrac{(n^2)!}{(n!)^{n+1}}\),显然为整数,证毕。

  2. 对于 \(n\in\mathbb{Z}\),证明 \(n^5\equiv n\pmod {10}\)

    鸽巢原理的奇特应用。

    等价于证明 \(10|(n^5-n)\),因式分解可得 \(n(n-1)(n+1)(n^2+1)\)

    变换形式,得\(n(n-1)(n+1)(n+2)(n+3)-(5n+5)n(n-1)(n+1)=n(n-1)(n+1)(n+2)(n+3)-5n(n-1)(n+1)^2\)

    由鸽巢原理,注意到以下两条基本性质:

    • 连续的 \(5\) 个数必定\(10\) 的倍数。
    • 连续的 \(2\) 个数必定\(2\) 的倍数。

    由此,设 \(n(n-1)(n+1)(n+2)(n+3)=10p,n(n-1)=2q\)

    上式即为 \(10p-5(n+1)^2\times 2q\)

    化简得 \(10(p-(n+1)^2q)\),显然是 \(10\) 的倍数,证毕。

1. 素数筛

注:也可以筛一些其他东西。

1.1. 埃氏筛

对于素数筛问题的模板题

埃氏筛的思想如下,若我们需要筛出小于等于 \(n\) 的素数,可以枚举 \(2\)\(n\) ,若当前枚举的数 \(k\) 是一个素数,则我们将 \(k\) 的倍数都标志为合数。特殊的,我们不去关心小于 \(k\)\(k\) 倍的数,因为该数在被 \(k\) 考虑之前一定被小于 \(k\) 的数考虑过。

可以证明,其时间复杂度为 \(O(n \log \log n)\)

以下代码实现筛出 \(n\) 以内的质数:

void prime(int n)
{
    int t=0;
    for(int i=2;i<=n;i++)
        isprime[i]=1;
    for(int i=2;i<=n;i++)
    {
        if(isprime[i])
        {
            prime[++t]=i;
            if(i*i<=n)
                for(int j=i*i;j<=n;j+=i)
                    isprime[j]=0;
        }
    }
}

1.2.欧拉筛

欧拉筛模板题

埃氏筛的时间复杂度为 \(O(n \log \log n)\),无法通过本题。

欧拉筛的时间复杂度为 \(O(n)\)

对于欧拉筛,每一个数会且仅会被筛到一次。

其做法是对于一个数 \(k\),遍历当前质数表 \(S\)\(S\) 呈单调递增),若 \(k \mod S_{i}=0\) 则停止遍历。

容易证明其正确性,若 \(k\mod S_{i}=0\),则说明 \(S_{i}\)\(k\) 的最小质因数。即 \(S_{i+1}\) 一定不是其最小质因数。那么 \(i\) 乘上 \(S_{i+a}\) 的结果一定会被 \(S_{i}\) 的倍数筛到。

附代码:

void prime(int n)
{
    int t=0;
    for(int i=2;i<=n;i++)
    {
        if(!chck[i])
            pri[++t]=i;
        for(int j=1;i*pri[j]<=n&&j<=t;j++)
        {
            chck[i*pri[j]]=1;
            if(i%pri[j]==0)
                break;

        }
    }
}

1.3. Miller Rabin

原理:基于费马小定理

\[a^{p-1} \equiv 1 \pmod p \]

给定若干组 \(a\),考虑上式是否成立。一般的,我们只需要取比 \(p\) 小的素数测试即可。

\(p\) 是一个奇素数,则有:

\[\begin{aligned}x^2&\equiv 1&\pmod p\\ x&\equiv1\operatorname{or}p-1&\pmod p \end{aligned} \]

证明:

\[\begin{aligned} x^2-1&\equiv 0 &\pmod p \\ (x+1)(x-1)&\equiv 0 &\pmod p \\ x&\equiv 1 \operatorname{or}p-1 &\pmod p \end{aligned} \]

综合以上定理,可以找到 \(k\) 个特殊的素数 \(p\) 代入公式进行验证即可判断素数,方法如下:

  1. \(p-1=m*2^k\),计算 \(q_0=p^m\),若 \(q_0= 1\),则通过。

  2. 否则设 \(q_i=q^{2}_{i-1}\),若 \(q_i=1 \operatorname{and} q_{i-1} \neq 1 \operatorname{or} p-1\),则一定是合数。

  3. \(q_k\) 不为 \(1\) 则为合数。

  4. 否则进行下一组测试。

  5. 所有测试通过即为素数。

bool miller_rubin(ll a,ll n)
{
    ll s=n-1,r=0;
    while((s&1)==0){
        s>>=1;r++;
    }
    ll k=qmod(a,s,n);
    if(k==1) return true;
    for(int i=0;i<r;i++,k=k*k%n){
        if(k==n-1) return true;
    }
    return false;
}
bool is_prime(ll n)
{
    ll times =_;
    ll prime[]={_};
    for(int i=0;i<times;i++){
        //miller_rubin();
    }
}

2. 裴蜀定理

裴蜀定理的描述如下:

对于整数 \(a,b\)\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=\gcd(a,b)\) 一定有解。

有以下推论:

  • 对于整数 \(a,b\)\(a,b\) 中至少有一个不为 \(0\)),方程 \(ax+by=d\) ,则 \(d=\gcd(a,b)\)(逆定理)。

  • 对于一个数列 \(a\)\(a\) 中元素不全为 \(0\)), \(\sum^{n}_{i=1} a_{i}x_{i}=\gcd^n_{i=1}a_{i}\) 一定有解。

下文给出了一个构造性证明。

3. 扩展欧几里得算法(exgcd)

3.1. 辗转相除法(欧几里得算法)

辗转相除法可以用来求解最大公因数等问题。

其主要算法流程如下:

  1. 给定两数 \(a,b\),我们认为 \(a>b\)

  2. \(b=0\) 返回 \(a\)。否则递归求解 \(\gcd(b,a \mod b)\)

算法正确性证明如下:

首先证明 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。

我们设 \(a=bk+c\),则 \(\gcd(a,a \mod b)=c\),设 \(d|a\)\(d|b\) ,则有 \(c=a-bk\)\(\dfrac{c}{d}=\dfrac{a}{d}-\dfrac{b}{d}k\)

不难发现 \(\dfrac{c}{d}\) 是整数,即 \(d|c\),所以 \(\gcd(a,b)\) 的答案是 \(gcd(b,a \mod b)\) 的答案。

逆命题容易证明。

Q.E.D

代码如下:

int gcd(int a,int b)
{
    if(b==0) return a;
    else return gcd(b,a%b);
}

对于两整数 \(a,b\),有:

\[\gcd(a,b) \times \operatorname{lcm}(a,b) = ab \]

可以从唯一分解定理的角度进行证明,即:

\[\begin{aligned} &\gcd(a,b)\times \operatorname{lcm}(a,b) \\ &=p_{1}^{\min(a_{1},b_{1})}p_{2}^{\min(a_{2},b_{2})}\cdots p_{k}^{\min(a_{k},b_{k})} p_{1}^{\max(a_{1},b_{1})}p_{2}^{\max(a_{2},b_{2})}\cdots p_{k}^{\max(a_{k},b_{k})} \\ &=p_1^{a_1+b_1}p_2^{a_2+b_2}\cdots p_k^{a_k+b_k} \\ &= ab \end{aligned} \]

由此,有:

int lcm(int a,int b)
{
    return a*b/gcd(a,b);
}

使用 <algorithm> 库中的 __gcd() 函数也可。

注意 <numeric> 库中的 std::gcdstd::lcm 仅在 C++ 17 及以上标准中可用。

3.2 扩展欧几里得算法

3.2.1. exgcd 求不定方程特解

扩展欧几里得算法(exgcd)常用于这样一类问题:给定一个不定方程 \(ax+by=c\),求其一组合法的整数解,即裴蜀定理。

即:求 \(ax+by=\gcd(a,b)\),并将求得的 \(x,y\) 分别乘以 \(\dfrac{c}{\gcd(a,b)}\)

我们可以尝试将问题转化为 4.1 中的欧几里得算法问题,即为 exgcd 算法。

与欧几里得法相似的,我们可以将 \(ax+by=\gcd(a,b)\) 递归为 \(bx'+(a \bmod b)y' = \gcd(b,a \bmod b)\)

特殊的,exgcd 中 \(ax+by=\gcd(a,b)\)\(bx'+(a \bmod b)y' = \gcd(b,a \bmod b)\) 并不等价。

由欧几里得算法:

\[bx'+(a \bmod b)y' = \gcd(a,b)\iff bx'+(a \bmod b)y' = \gcd(b,a \mod b) \]

假设 \(bx'+(a \bmod b)y ' = \gcd(a,b)\) 中,\(x',y'\) 已知,由模运算定义:

\[ay'+b(x'-\lfloor\frac{a}{b}\rfloor y')=\gcd(a,b) \]

所以

\[x=y',y=x'-\lfloor\frac{a}{b}\rfloor y' \]

由定义,\(\gcd(a,0)=a\),此时 \(x=1,y=0\)

所以我们只需要递归到 \(b=0\) 时终止即可。

于是我们求出了不定方程 \(ax+by=c\) 的一组整数解,这也间接地证明了裴蜀定理。

附代码:

int exgcd(int a,int b,int &x,int &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    }
    int h = exgcd(b, a % b, y, x);
    y -=  a / b * x;
    return h;
}

3.2.2. exgcd 求不定方程通解

假设我们已经知道了不定方程 \(ax+by=c\) 的一组解 \(x_{0},y_{0}\),若 \(x_{0}\) 增加 \(r\) 则方程左边增加 \(ar\),为保证等式成立,\(by\) 需减去 \(ar\),同时,\(y\) 是整数。这意味着 \(ar\)\(b\) 的倍数。

我们不妨设 \(t=ar\),因为 \(ar\)\(b\) 的倍数,所以最小的 \(t=\operatorname{lcm}(a,b)\)

由最小公倍数性质:

\[\operatorname{lcm}(a,b)=ar=\dfrac{ab}{\gcd(a,b)} \]

带回原式,得:

\[r=\dfrac{b}{\gcd(a,b)} \]

\(y\) 需减去:

\[\dfrac{ar}{b}=\dfrac{\frac{ab}{\gcd(a,b)}}{b}=\dfrac{a}{\gcd(a,b)} \]

所以,不定方程 \(ax+by=c\) 的通解是:

\[x=x_{0}+\dfrac{b}{\gcd(a,b)}k,y=y_{0}-\dfrac{a}{\gcd(a,b)}k \]

3.2.3 不定方程的无解情况

这与裴蜀定理所讨论的情况不同

对于不定方程 \(ax+by=c\) 其有解的必要条件是 \(c \bmod \gcd(a,b) = 0\)

证明如下:由最大公因数定义,\(a,b\) 都是 \(\gcd(a,b)\) 的倍数,所以 \(ax+by\)\(\gcd(a,b)\) 的倍数,即 \(c\)\(\gcd(a,b)\) 的倍数。若 \(c \bmod \gcd(a,b) \ne 0\),与 \(c\)\(\gcd(a,b)\) 的倍数矛盾,不成立。此时不定方程无解。

4. 同余方程

同余方程模板题

同余方程指的是形如 \(ax \equiv 1 \pmod b\) 的方程。

对于这种方程,我们可以将其转化为上文的不定方程 \(ax+by=c\)

为什么?

\(a \equiv b \pmod p\) 的本质是 \(a\)\(b\) 之间相差了若干个 \(p\)

所以 \(ax \equiv 1 \pmod b \iff ax+by \equiv 1\)

特殊的,与不定方程类似,同余方程同样可能无解,这发生在 \(a,p\) 不互质的情况,证明如下:

对于不定方程 \(ax+by=1\),其有解的条件是 \(1 \bmod \gcd(a,b)=0\),即 \(\gcd(a,b) =1\)

5. 费马小定理

费马小定理的描述如下:若 \(p\) 为素数,\(\gcd(a,p)=1\),则 \(a^{p-1}\equiv 1 \pmod p\)

费马小定理是欧拉定理的一个推论,将在下文证明其正确性。

一个简单地应用:若 \(p\) 是素数,\(\gcd(a,p)=a\) ,则:

\[a^b\equiv a^{b \bmod p-1}\pmod p \]

另一个不简单的应用是 Miller-Rabbin 算法。

6.欧拉定理

6.1 欧拉定理

定义 \(\varphi(i)\)\(1 \sim n\) 中与 \(i\) 互质的数的个数。

欧拉定理如下:

\(\gcd(a,p)=1\),则有:

\[a^{\varphi(p)}\equiv 1 \pmod p \]

证明如下:

引理: \(r_{1},r_{2},\dots,r_{\varphi(p)}\)\(p\) 的一个简化剩余系,且 \(\gcd(a,p)=1\)\(ar_{1},ar_{2},\dots,ar_{\varphi(p)}\) 也为 \(p\) 的一个简化剩余系。

证明:\(\forall 1\le i\le \varphi(p)\),有 \(\gcd(a,r_{i})=\gcd(p,r_{i})=1\)

所以 \(\gcd(ap,r_{i})=1\),即该元素仍存在于剩余系中。

原命题等价于:

\[\prod_{i=1}^{\varphi(p)}r_{i} \equiv\prod_{i=1}^{\varphi(p)}(ar_{i})\pmod p \]

两遍同时约掉 \(\prod_{i=1}^{\varphi(p)} r_i\),得:

\[a^{\varphi(p)}\equiv 1 \pmod i \]

Q.E.D.

6.2. 扩展欧拉定理

模板题

考虑没有对 \(\gcd(a,m)\) 限定的情况,不带证明的,给出:

\[a^b\equiv \begin{cases} a^{b\bmod\varphi(m)}&\gcd(a,b)=1\\ a^{b\bmod\varphi(m)+\varphi(m)} &\gcd(a,b)\neq1\operatorname{and}b\ge \varphi(m)\\ a^b&\gcd(a,b)\neq1 \operatorname{and}b<\varphi(m) \end{cases} \]

结论背过就行。

7. 模意义下的乘法逆元

所以我们有必要先知道什么是模意义下的乘法逆元(本文不讨论其它逆元,下文逆元皆指“模意义下的乘法逆元”)。

给定整数 \(a,p\),若 \(ax \equiv 1\pmod p\),则称 \(x\)\(a\) 的逆元。

如果抛弃模意义,乘法逆元其实就是取其倒数。

在下文中可能用 \(a^{-1}\) 表示 \(a\) 的逆元。

7.1. exgcd求逆元

逆元模板题

显然可以用 4. 中提到的同余方程求解。

需要注意的是,由于不定方程可能无解,所以逆元只存在于 \(a,p\) 互质时。

值得注意的是,这里仅要求 \(a,p\) 互质,而费马小定理还要求 \(p\) 一定为质数。

代码:

void exgcd(int a, int b, int &x, int &y) 
{
    if (!b) 
        x=1,y=0;
    else 
        exgcd(b,a%b,y,x),y-= a/b*x;
}
exgcd(a,p,x,y);//a 在 mod p 下的逆元为 x。

7.2. 费马小定理求逆元

由费马小定理,\(a^{p-1}\equiv 1 \pmod p\)

而逆元要求的式子是 \(ax \equiv 1 \pmod p\)

不难发现 \(x=a^{p-2}\),快速幂求出即可。

求一个数的逆元的时间复杂度 \(O(\log n)\),可以在模板题中拿到 64 pts。

7.3. 线性求逆元

如果我们需要求 \(1 \sim n\) 中所有数的逆元,时间复杂度为 \(O(n\log n)\),不可接受。

可以在线性时间复杂度求解该问题。

显然,对于任意的 \(p\),$1^{-1} \equiv 1\pmod p $

我们可以递归的求解 \(i^{-1}\)

\(k=\lfloor \dfrac{p}{i} \rfloor,j=p \bmod i\),则:

\[p=ki+j \]

将其代回模 \(p\) 意义下,得:

\[ki+j \equiv 0 \pmod p \]

两遍同乘 \(i^{-1}j^{-1}\),得:

\[kj^{-1}+i^{-1} \equiv 0 \pmod p \]

移项,得:

\[i^{-1} \equiv -kj^{-1} \pmod p \]

即:

\[i^{-1} \equiv -\lfloor \dfrac{p}{i} \rfloor (p \bmod i)^{-1} \]

我们可以发现,\((p \bmod i)^{-1}\) 一定会在 \(i^{-1}\) 之前求出,所以我们可以递推地求逆元。

特殊的,当 \(p\equiv 0 \pmod i\)\(p \bmod i\) 未定义,这是由于在 \(p\)\(i\) 不互质时,没有逆元导致的,所以我们一般会将 \(p\) 设置为大质数,以防止这种情况的发生,这也是部分数论题的模数为大质数的原因。

代码:

inv[1]=1;
for(int i=2;i<=n;i++)
{
    inv[i]=(p-p/i)*inv[p%i]%p;
}

我们使用 p-p/i 来避免出现负数。

7.4. 线性求任意 \(n\) 个数逆元

模板题

时间复杂度要求 \(O(n)\)

这与 7.3 没有关系。

\(s_{n}=\prod^{n}_{i=1}a_{i}\),并计算 \(s_{n}^{-1}\)

显然,有 \(s_{i-1}^{-1}=s_{i}^{-1}a_{i}\),故可以递推计算 \(s_{i}^{-1}\)

\(a_{i}^{-1}=s_{i}^{-1} \times s_{i-1}\)。原问题得解。

代码:

s[0]=1;
for(int i=1;i<=n;i++)
    s[i]=s[i-1]*a[i]%p;
invs[n]=qpow(s[n],p-2);//请注意题目所给是否是质数
for(int i=n;i>=1;i--)
    invs[i-1]=invs[i]*a[i]%p;
for(int i=1;i<=n;i++)
    inva[i]=invs[i]*s[i-1]%p;

8.卢卡斯定理

8.1 模数为质数的情况

模板题

卢卡斯定理可以解决一些大组合数取模问题。

对于质数 \(p\),有:

\[\binom{n}{m}\bmod p=\binom{\lfloor\frac{n}{p}\rfloor}{\lfloor\frac{m}{p}\rfloor}\binom{n\bmod p}{m\bmod p}\bmod p \]

在此不做证明。

值得注意的,\(\binom{\lfloor\frac{n}{p}\rfloor}{\lfloor\frac{m}{p}\rfloor}\) 可继续递归求解,直至 \(m=0\) 为止。

int solve(int n, int m) {
    if (m == 0) {
        return 1;
    }
    else {
        return solve(n / p, m / p) % p * C(n % p, m % p) % p;
    }
}

8.2 exLucas

模板题

需先阅读 9.1。

事实上,\(p\) 为小合数时,也有类似的做法,这被称为 exlucas,即求:

\[C^m_n\bmod p \]

由于 \(p\) 是合数,我们不妨先使用 CRT/exCRT进行拆分,设 \(p=p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_t^{\alpha_t}\),得:

\[\begin{equation} \begin{cases} C^m_n&\bmod& p_1^{\alpha_1}\\ &\cdots\\ C^m_n&\bmod& p_t^{\alpha_t} \end{cases} \end{equation} \]

问题转化为求解 \(C^m_n\bmod p^{k},p\in\texttt{prime}\)

展开,得:

\[\begin{equation} \dfrac{n!}{m!(n-m)!}\bmod p^{k} \end{equation} \]

我们无法直接计算 \(m!(n-m)!\) 的逆元,但由于 \(p\) 是质数,所以可以变形为:

\[\begin{equation} \dfrac{\frac{n!}{p^x}}{\frac{m!}{p^y}\frac{(n-m)!}{p^z}}p^{x-y-z}\bmod p^{k} \end{equation} \]

其中,\(x,y,z\) 指分子含有因数 \(p\) 的个数。

由于两数互质是存在逆元的充分必要条件,上式的分母一定存在模 \(p^{k}\) 意义下的逆元。

即我们将问题进一步转化为求:

\[\begin{equation} \dfrac{n!}{p^x}\bmod p^{k} \end{equation} \]

在这个式子中,我们考虑 \(n!\) 是由哪几部分组成的。借用 oi-wiki 的例子,我们以 \(n=22,p=3,k=2\) 进行举例:

\[\begin{aligned} 22! =&(1\times2\times4\times5\times7\times8)\times(10\times11\times13\times14\times16\times17)\\ &\times(19\times20)\\&\times(3\times6\times9\times12\times15\times18\times21) \\ =&(1\times2\times4\times5\times7\times8)^2\times(19\times20)\times3^77! \end{aligned} \]

观察最后的这个式子,它由三部分组成:

  1. \(3\) 的幂,其次数是 \(\lfloor\frac{n}{p}\rfloor\)
  2. \(\lfloor\frac{n}{p}\rfloor!\) 它和第一部分共同构成了与 \(p\) 互质部分的乘积。
  3. \(n!\) 中,与 \(p\) 不互质部分的乘积,首先是每 \(p^k\) 个一组,共 \(\lfloor\frac{n}{p}\rfloor\) 组,之后是无法被完整归到组内的剩余部分。

形式化的,\(n!\pmod {p^{k}}\Longleftrightarrow\\\)

\[\begin{equation} p^{\lfloor\frac{n}{p}\rfloor}\lfloor\frac{n}{p}\rfloor! (\prod_{i=1,i\bmod p\neq0}^{p^k} i)^{\lfloor\frac{n}{p^k}\rfloor}(\prod_{i=p^k\lfloor\frac{n}{p^k}\rfloor,i\pmod p \neq 0}^n i) \end{equation} \]

由于 \(\lfloor\dfrac{n}{p}\rfloor\) 恰好等于 \(x\),将 \((5)\) 式变形,定义函数 \(f(n)\) 为:

\[\begin{equation} f(n)=\lfloor\frac{n}{p}\rfloor! (\prod_{i=1,i\bmod p\neq0}^{p^k} i)^{\lfloor\frac{n}{p^k}\rfloor}(\prod_{i=p^k\lfloor\frac{n}{p^k}\rfloor,i\pmod p \neq 0}^n i)\pmod {p^k} \end{equation} \]

由于 \(\lfloor\dfrac{n}{p}\rfloor\) 中还与 \(p^k\) 存在公因数,所以该部分可以递归求解,边界为 \(f(0)=1\)

现在我们考虑如何求解 \((4)\) 式中的 \(x\)

由于 \(x\) 恰好等于 \(\lfloor\dfrac{n}{p}\rfloor\),所以可定义函数 \(g(x)\) 为:

\[\begin{equation} g(x)=g(\lfloor\frac{n}{p}\rfloor)+\lfloor\frac{n}{p}\rfloor \end{equation} \]

\((7)\) 式代回 \((3)\) 式,得:

\[\begin{equation} \dfrac{\frac{n!}{p^x}}{\frac{m!}{p^y}\frac{(n-m)!}{p^z}}p^{g(n)-g(m)-g(n-m)}\bmod p^{k} \end{equation} \]

\((3)\) 式的答案通过 CRT/exCRT 合并,即可得到 \((1)\) 式答案。

int f(int n, int mod, int k) {//k 底数 n当前分解 mod 完整的模数(p^k)
    if (n == 0) {
        return 1;
    }
    int ans = 1;
    for (int i = 1; i <= mod; i++) {
        if (i % k != 0) {
            ans = ans * i  % mod;
        }
    } 
    ans = qpow(ans, n / mod, mod);
    for (int i = mod * (n / mod); i <= n; i++) {
        if (i % k != 0) {
            ans = ans * (i % mod) % mod;
        }
    }
    ans = (ans * f(n / k, mod, k)) % mod;
    return ans; 
}

int g(int n, int k) {
    if (n < k) {
        return 0;
    }
    return g(n / k, k) + n / k;
}

int solve(int k, int mod) {
    return f(n, mod, k) * inv(f(m, mod, k), mod) % mod * inv(f(n-m, mod, k), mod) % mod * qpow(k, g(n, k) - g(m, k) - g(n - m, k), mod) % mod;
}

int exlucas(int n, int m, int mod) {
    int upp = sqrt(mod);
    for (int i = 2; i <= upp; i++) {
        if (mod % i == 0) {
            ++top;
            pri[top] = i;
            while (mod > 0 && mod % i == 0) {
                pnum[top] ++;
                mod /= i;
            }
        }
    }
    if (mod != 1) {
        pri[++ top] = mod;
        pnum[top] ++;
    }
    for (int i = 1; i <= top; i++) {
        ans[i] = solve(pri[i], qpow(pri[i], pnum[i]));
    }
    int res = ans[1], resmod = qpow(pri[1], pnum[1]);
    for (int i = 2; i <= top; i++) {
        excrt(res, resmod, ans[i], qpow(pri[i], pnum[i]));
    }
    return res;
}

9.中国剩余定理(CRT)

9.1. 模数互质的情况

模板题

中国剩余定理可以求解如下的方程组的解,:

\[\begin{cases} x\equiv a_{1}\pmod{p_{1}} \\ x\equiv a_{2}\pmod{p_{2}} \\ \cdots \\ x\equiv a_{k} \pmod{p_{k}} \end{cases} \]

其中 \(p\) 两两互质。

我们可以构造地解出该方程组。

  1. \(P=\prod_{i=1}^{k}p_{i}\)

  2. \(m_{i}=\dfrac{P}{p_{i}}\)

  3. 求解 \(m_{i}\)\(\pmod {p_{i}}\) 意义下的逆元 \(m_{i}^{-1}\)

  4. \(c_{i}=m_{i}m_{i}^{-1}\)

答案即为 \(\sum_{i=1}^{k} a_{i}c_{i} \pmod {P}\)

证明如下:

\(i \ne j\),则:

\[c_{j}\equiv m_{j}\equiv 0 \pmod {p_{i}} \]

又有:

\[c_{i}\equiv m_{i}(m_{i}^{-1}\bmod p_{i})\equiv 1\pmod {p_{i}} \]

代入原式,得:

\[\begin{aligned}x &\equiv \sum_{j=1}^{k} a_{j}c_{j} &&\pmod {p_{i}}\\&\equiv a_{i}c_{i}&&\pmod {p_{i}}\\&\equiv a_{i}m_{i}(m_{i}^{-1}\bmod p_{i})&&\pmod {p_{i}}\\&\equiv a_{i}&&\pmod {p_{i}}\end{aligned} \]

故对于 \(\forall 1\le i\le k,x\equiv a_{i}\pmod {p_{i}}\)

证毕!

CRT 代码:

int CRT()
{
    int sump=0,ans=0;
    for(int i=1;i<=n;i++)
        sump*=p[i];
    for(int i=1;i<=n;i++)
    {
        int b,y;
        m[i]=sump/p[i];
        exgcd(m[i],p[i],b,y);
        invm[i]=b;
        c[i]=invm[i]*m[i];
        ans+=a[i]*c[i]%sump;
    }
    return (ans%sump+sump)%sump;
}

CRT 告诉了我们这样一件事,对于两两互质的数列 \(p_{1},p_{2},\cdots,p_{k}\)\(P=0\sim \prod_{i=1}^{k}p_{i}\)\(P\) 个数与 \([0,m_{1}),[0,m_2),\cdots,[0,m_k)\) 这样的 \(k\) 个二元组之间存在一一对应关系。

也就是说当我们想要求一个在 \(\prod_{i=1}^{k}p_i\) 之内的数时,我们只需要求其分别对 \(p_1,p_2,\cdots,p_k\) 取模的值,再使用 CRT 合并即可。

把 P 分解为质因数整数幂的成绩,对于每个质因数整数幂作为模数分别求解(这往往比任意合数好求),再用 CRT 合并,是数论题常见的解法——@zhqwq

这类问题的模板题:[SDOI2010] 古代猪文

9.2. exCRT

模板题

求解:

\[\begin{equation} \begin{cases} x\equiv a_{1}\pmod{p_{1}} \\ x\equiv a_{2}\pmod{p_{2}} \\ \cdots \\ x\equiv a_{k} \pmod{p_{k}} \end{cases} \end{equation} \]

的正整数解,其中 \(p\) 不保证两两互质。

考虑前两个方程,将其合并,我们有:

\[\begin{equation} \begin{aligned} x'=a_1+m_1p=a_2+m_2q\\ m_1p-m_2q =a_1+a_2 \end{aligned} \end{equation} \]

由裴蜀定理,当 \(\gcd(m_1,m_2)|(a_1+a_2)\) 时,该方程有解。

讨论有解情况时,可以将两边同时除以 \(\gcd(m1,m2)\),得:

\[\begin{equation} \begin{cases} k_1&=\dfrac{m_1}{\gcd(m1,m2)}\\\\ k_2&=\dfrac{m_2}{\gcd(m1,m2)}\\\\ y&=\dfrac{(a_1+a_2)}{\gcd(m1,m2)} \end{cases} \end{equation} \]

得到 \(k_1p+k_2q=y\),由于 \(k1,k2\) 互质,所以我们可以得出一组它的解 \((u,i)\),然后有:

\[\begin{equation} \begin{cases} p=uy\\ q=-iy \end{cases} \end{equation} \]

对于式 \((10)\),我们可以将式 \((12)\) 代入得到:

\[x'=a_1+m_1p=a_1+m_1uy \]

代入式 \((11)\) 得:

\[x'=a_1+m_1u\dfrac{(a_1+a_2)}{\gcd(m1,m2)} \]

至此,我们完成了两个同余方程的合并,并得到了特解。容易发现,新的同余式子是在 \(\bmod \operatorname{lcm}(m1,m2)\) 意义下的。

因通解是容易求的,为:

\[\begin{equation} x\equiv x'\pmod {\operatorname{lcm}(m1,m2)} \end{equation} \]

我们不妨去想一想这是为什么,对 \(\operatorname{lcm}(m1,m2)\) 取模的结果,就是将整个整数集分成了 \(\operatorname{lcm}(m1,m2)\) 个等价类,如果等价类里有 \(1\) 个解,那肯定所有都是解。

一人得道,鸡犬升天。

——阮行止

按该方法进行合并即可。

void excrt(int &a, int &mod, int aa, int mmod) {
    int g = __gcd(mod, mmod);
    int m = mod / g * mmod;
    int p, q;
    exgcd(mod / g, mmod / g, p, q);
    p = p * mod % m * (aa - a) / g % m;
    a = (a + p + m) % m;
    mod = m;
}

wrote on 23.11.3

upd on 23.11.28

upd on 23.12.1

upd on 24.1

upd on 24.9.1 这回应该真写完了。

posted @ 2025-08-15 23:33  Rosentツ  阅读(15)  评论(0)    收藏  举报