数论及其相关 学习笔记

数论及其相关 学习笔记

todo

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

目录

  1. 前置知识与符号定义

  2. 素数筛

  3. 裴蜀定理

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

  5. 同余方程

  6. 费马小定理

  7. (扩展)欧拉定理

  8. 模意义下的乘法逆元

  9. (扩展)卢卡斯定理

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

0.前置知识与符号定义

在无特殊说明的情况下,所有数字均 Z,且一般 Z+

0.0 唯一分解定理

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

t=p1k1p2k2p3k3piki

其中 p 是互不相同的素数。

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

0.1. 素数

对于一个数 pp0,±1,且 p 没有除 1p 以外的因数,则认为 p 是素数,否则认为 p 是合数。

0.2. 最大公因数

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

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

由于:

a=p1k1p2k2p3k3pikib=p1k1p2k2p3k3piki

我们有:

gcd(a,b)=p1min(k1,k1)p2min(k2,k2)pimin(ki,ki)

p 仍是互不相同的质数。

lcm 可以同理定义。

0.3. 整除符号

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

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

d|a,则 amodd=0,即 a=dq,qZ

0.4. 模运算

定义 amodb=aab×b

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

有以下性质:

(a±b)modm=(amodm±bmodm)modm

a×bmodm=(amodm×bmodm)modm

证明显然。

abamodmbmodmmodm

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

0.5. 同余

amodm=bmodm,则称 a,b 同余,即 ab(modm)

有以下性质:

m|(ab)ab(modm)

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

ab(modm)cd(modm)a±cb±d(modm)

ab(modm)bc(modm)abc(modp)

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

ab(modm)a±kb±k(modm)

ab(modm)akbk(modm)

ab(modm)anbn(modm)

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

gcd(c,m)=1,则:

acbc(modm)ab(modm)

证明:

acbc(modm)m|(acbc)m|c(ab)gcd(c,m)=1m|(ab)ab(modm)

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

acbc(modm)ab(modmd)

证明:

acbc(modm)m|c(ab)

若有整数 k 使得 (ab)c=mk,则

m|c(ab)(ab)cd=mkdgcd(cd,md)=1md|(ab)ab(modmd)

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

0.6. 快速幂

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

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

对于快速幂的问题定义如下:给定两个整数 a,b,求 abmodk

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

我们不妨将指数采用二进制表述,设指数的二进制串为 Sitmp 表示当前二进制位对答案的贡献,a 表示底数。

Sk=1,则 ansans×tmp。由于第 k 位对答案的贡献为a2k1×a2k1,即 atmp×atmp,化简得 atmp2 所以我们在每次操作后使 tmptmp×tmp 即可。

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

代码:

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. 对于 nZ,证明 (n2)!(n!)n+1Z

    组合意义保平安!

    考虑以下问题情景,将 n2 个人分成 n 组,每组 n 人,求方案总数。

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

    所以这个问题的答案即为 (n2)!(n!)n+1,显然为整数,证毕。

  2. 对于 nZ,证明 n5n(mod10)

    鸽巢原理的奇特应用。

    等价于证明 10|(n5n),因式分解可得 n(n1)(n+1)(n2+1)

    变换形式,得n(n1)(n+1)(n+2)(n+3)(5n+5)n(n1)(n+1)=n(n1)(n+1)(n+2)(n+3)5n(n1)(n+1)2

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

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

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

    上式即为 10p5(n+1)2×2q

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

1. 素数筛

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

1.1. 埃氏筛

对于素数筛问题的模板题

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

可以证明,其时间复杂度为 O(nloglogn)

以下代码实现筛出 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(nloglogn),无法通过本题。

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

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

其做法是对于一个数 k,遍历当前质数表 SS 呈单调递增),若 kmodSi=0 则停止遍历。

容易证明其正确性,若 kmodSi=0,则说明 Sik 的最小质因数。即 Si+1 一定不是其最小质因数。那么 i 乘上 Si+a 的结果一定会被 Si 的倍数筛到。

附代码:

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

原理:基于费马小定理

ap11(modp)

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

p 是一个奇素数,则有:

x21(modp)x1orp1(modp)

证明:

x210(modp)(x+1)(x1)0(modp)x1orp1(modp)

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

  1. p1=m2k,计算 q0=pm,若 q0=1,则通过。

  2. 否则设 qi=qi12,若 qi=1andqi11orp1,则一定是合数。

  3. qk 不为 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,ba,b 中至少有一个不为 0),方程 ax+by=gcd(a,b) 一定有解。

有以下推论:

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

  • 对于一个数列 aa 中元素不全为 0), i=1naixi=gcdi=1nai 一定有解。

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

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

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

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

其主要算法流程如下:

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

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

算法正确性证明如下:

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

我们设 a=bk+c,则 gcd(a,amodb)=c,设 d|ad|b ,则有 c=abkcd=adbdk

不难发现 cd 是整数,即 d|c,所以 gcd(a,b) 的答案是 gcd(b,amodb) 的答案。

逆命题容易证明。

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)×lcm(a,b)=ab

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

gcd(a,b)×lcm(a,b)=p1min(a1,b1)p2min(a2,b2)pkmin(ak,bk)p1max(a1,b1)p2max(a2,b2)pkmax(ak,bk)=p1a1+b1p2a2+b2pkak+bk=ab

由此,有:

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 分别乘以 cgcd(a,b)

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

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

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

由欧几里得算法:

bx+(amodb)y=gcd(a,b)bx+(amodb)y=gcd(b,amodb)

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

ay+b(xaby)=gcd(a,b)

所以

x=y,y=xaby

由定义,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 的一组解 x0,y0,若 x0 增加 r 则方程左边增加 ar,为保证等式成立,by 需减去 ar,同时,y 是整数。这意味着 arb 的倍数。

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

由最小公倍数性质:

lcm(a,b)=ar=abgcd(a,b)

带回原式,得:

r=bgcd(a,b)

y 需减去:

arb=abgcd(a,b)b=agcd(a,b)

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

x=x0+bgcd(a,b)k,y=y0agcd(a,b)k

3.2.3 不定方程的无解情况

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

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

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

4. 同余方程

同余方程模板题

同余方程指的是形如 ax1(modb) 的方程。

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

为什么?

ab(modp) 的本质是 ab 之间相差了若干个 p

所以 ax1(modb)ax+by1

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

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

5. 费马小定理

费马小定理的描述如下:若 p 为素数,gcd(a,p)=1,则 ap11(modp)

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

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

ababmodp1(modp)

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

6.欧拉定理

6.1 欧拉定理

定义 φ(i)1n 中与 i 互质的数的个数。

欧拉定理如下:

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

aφ(p)1(modp)

证明如下:

引理: r1,r2,,rφ(p)p 的一个简化剩余系,且 gcd(a,p)=1ar1,ar2,,arφ(p) 也为 p 的一个简化剩余系。

证明:1iφ(p),有 gcd(a,ri)=gcd(p,ri)=1

所以 gcd(ap,ri)=1,即该元素仍存在于剩余系中。

原命题等价于:

i=1φ(p)rii=1φ(p)(ari)(modp)

两遍同时约掉 i=1φ(p)ri,得:

aφ(p)1(modi)

Q.E.D.

6.2. 扩展欧拉定理

模板题

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

ab{abmodφ(m)gcd(a,b)=1abmodφ(m)+φ(m)gcd(a,b)1andb<φ(m)abgcd(a,b)1andbφ(m)

结论背过就行。

7. 模意义下的乘法逆元

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

给定整数 a,p,若 ax1(modp),则称 xa 的逆元。

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

在下文中可能用 a1 表示 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. 费马小定理求逆元

由费马小定理,ap11(modp)

而逆元要求的式子是 ax1(modp)

不难发现 x=ap2,快速幂求出即可。

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

7.3. 线性求逆元

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

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

显然,对于任意的 p111(modp)

我们可以递归的求解 i1

k=pi,j=pmodi,则:

p=ki+j

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

ki+j0(modp)

两遍同乘 i1j1,得:

kj1+i10(modp)

移项,得:

i1kj1(modp)

即:

i1pi(pmodi)1

我们可以发现,(pmodi)1 一定会在 i1 之前求出,所以我们可以递推地求逆元。

特殊的,当 p0(modi)pmodi 未定义,这是由于在 pi 不互质时,没有逆元导致的,所以我们一般会将 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 没有关系。

sn=i=1nai,并计算 sn1

显然,有 si11=si1ai,故可以递推计算 si1

ai1=si1×si1。原问题得解。

代码:

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,有:

(nm)modp=(npmp)(nmodpmmodp)modp

在此不做证明。

值得注意的,(npmp) 可继续递归求解,直至 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,即求:

Cnmmodp

由于 p 是合数,我们不妨先使用 CRT/exCRT进行拆分,设 p=p1α1p2α2ptαt,得:

(1){Cnmmodp1α1Cnmmodptαt

问题转化为求解 Cnmmodpk,pprime

展开,得:

(2)n!m!(nm)!modpk

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

(3)n!pxm!py(nm)!pzpxyzmodpk

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

由于两数互质是存在逆元的充分必要条件,上式的分母一定存在模 pk 意义下的逆元。

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

(4)n!pxmodpk

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

22!=(1×2×4×5×7×8)×(10×11×13×14×16×17)×(19×20)×(3×6×9×12×15×18×21)=(1×2×4×5×7×8)2×(19×20)×377!

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

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

形式化的,n!(modpk)

(5)pnpnp!(i=1,imodp0pki)npk(i=pknpk,i(modp)0ni)

由于 np 恰好等于 x,将 (5) 式变形,定义函数 f(n) 为:

(6)f(n)=np!(i=1,imodp0pki)npk(i=pknpk,i(modp)0ni)(modpk)

由于 np 中还与 pk 存在公因数,所以该部分可以递归求解,边界为 f(0)=1

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

由于 x 恰好等于 np,所以可定义函数 g(x) 为:

(7)g(x)=g(np)+np

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

(8)n!pxm!py(nm)!pzpg(n)g(m)g(nm)modpk

(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. 模数互质的情况

模板题

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

{xa1(modp1)xa2(modp2)xak(modpk)

其中 p 两两互质。

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

  1. P=i=1kpi

  2. mi=Ppi

  3. 求解 mi(modpi) 意义下的逆元 mi1

  4. ci=mimi1

答案即为 i=1kaici(modP)

证明如下:

ij,则:

cjmj0(modpi)

又有:

cimi(mi1modpi)1(modpi)

代入原式,得:

xj=1kajcj(modpi)aici(modpi)aimi(mi1modpi)(modpi)ai(modpi)

故对于 1ik,xai(modpi)

证毕!

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 告诉了我们这样一件事,对于两两互质的数列 p1,p2,,pkP=0i=1kpiP 个数与 [0,m1),[0,m2),,[0,mk) 这样的 k 个二元组之间存在一一对应关系。

也就是说当我们想要求一个在 i=1kpi 之内的数时,我们只需要求其分别对 p1,p2,,pk 取模的值,再使用 CRT 合并即可。

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

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

9.2. exCRT

模板题

求解:

(9){xa1(modp1)xa2(modp2)xak(modpk)

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

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

(10)x=a1+m1p=a2+m2qm1pm2q=a1+a2

由裴蜀定理,当 gcd(m1,m2)|(a1+a2) 时,该方程有解。

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

(11){k1=m1gcd(m1,m2)k2=m2gcd(m1,m2)y=(a1+a2)gcd(m1,m2)

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

(12){p=uyq=iy

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

x=a1+m1p=a1+m1uy

代入式 (11) 得:

x=a1+m1u(a1+a2)gcd(m1,m2)

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

因通解是容易求的,为:

(13)xx(modlcm(m1,m2))

我们不妨去想一想这是为什么,对 lcm(m1,m2) 取模的结果,就是将整个整数集分成了 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 @   ChthollyNS  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示