本系列文章将于2021年整理出版。前驱教材:《算法竞赛入门到进阶》 清华大学出版社
网购:京东 当当   作者签名书:点我
公众号同步:算法专辑   
暑假福利:胡说三国
有建议请加QQ 群:567554289

文章目录

  • 1. 同余概述
    • 1.1. 同余定义
    • 1.2. 一些定理和性质
  • 2. 一元线性同余方程
  • 3. 逆
    • 3.1.逆的概念
    • 3.2.求逆
    • 3.3. 用逆求解同余方程
    • 3.4. 逆与除法取模
  • 4. 同余方程组
    • 4.1. 中国剩余定理
    • 4.2. 迭代法

   同余是很巧妙的工具,它使得人们能够用等式的形式来简洁地描述整除关系。
   在阅读本节内容时,请对照上一节“线性丢番图方程”的内容,有很多类似的地方。

1. 同余概述

1.1. 同余定义

  设m是正整数,若a和b是整数,且m∣(a−b)m | (a - b)m∣(a−b),则称aaa和bbb模mmm同余。也就是说,aaa除以mmm得到的余数,和bbb除以mmm的余数相同;或者说,a−ba - ba−b除以mmm,余数是0。
  把aaa和bbb模mmm同余记为 a≡b(modm)a\equiv b (mod m)a≡b(modm),mmm称为同余的模。例子:
  (1)因为7|(18-4),所以18≡\equiv≡ 4 (mod 7),18除以7余数是4,4除以7的余数也是4;
  (2)3≡\equiv≡ 6 (mod 9),3除以9余数是3,-6除以9的余数也是3;
  (3)13和5模9不同余,因为13除以9余数是4,5除以9余数是5。

1.2. 一些定理和性质

  (1)若aaa和bbb是整数,mmm为正整数,则a≡b(modm)a\equiv b (mod\ m)a≡b(mod m)当且仅当amodm=bmodma\ mod\ m = b mod\ ma mod m=bmod m。
  (2)把同余式转换为等式。若aaa和bbb是整数,则a≡b(modm)a\equiv b (mod\ m)a≡b(mod m)当且仅当存在整数,使得a=b+kma = b + kma=b+km。例如:19-2 (mod 7),有19 = -2 + 3 ∙ 7。
  (3)设mmm是正整数,模mmm的同余满足下面的性质:
  1)自反性。若aaa是整数,则a≡a(modm)a\equiv a (mod\ m)a≡a(mod m)
  2)对称性。若aaa和bbb是整数,且a≡b(modm)a\equiv b (mod\ m)a≡b(mod m),则b≡a(modm)b\equiv a (mod\ m)b≡a(mod m)。
  3)传递性。若a、b、ca、b、ca、b、c是整数,且a≡b(modm)和b≡c(modm)a\equiv b (mod\ m)和b\equiv c (mod m)a≡b(mod m)和b≡c(modm),则a≡c(modm)a\equiv c (mod\ m)a≡c(mod m)。

2. 一元线性同余方程

  一元线性同余方程:设xxx是未知数,给定a、b、ma、b、ma、b、m,求整数xxx,满足 ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)。
  研究线性同余方程有什么用处?ax≡b(modm)ax\equiv b(mod\ m)ax≡b(mod m)表示ax−bax - bax−b是mmm的倍数,设为 −y-y−y倍,则有ax+my=bax + my = bax+my=b,这就是二元线性丢番图方程。所以,求解一元线性同余方程等同于求解二元线性丢番图方程。
  方程是否有解?如果有解,有多少解?如何求出解?与线性丢番图的定理一样,线性同余方程也有类似的定理。
  定理:设a,ba,ba,b和mmm是整数,m>0,gcd(a,m)=dm > 0,gcd(a, m) = dm>0,gcd(a,m)=d,若ddd不能整除b,则ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)无解,若ddd能整除bbb,则ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)有ddd个模mmm不同余的解。
  定理的前半部分可以概况为:ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)有解的充分必要条件是gcd(a,m)gcd(a, m)gcd(a,m)能整除b。
  定理的后半部分说明了解的情况。与线性丢番图方程类似,如果有一个特解是 x0x_0x0​ ,那么通解是x=x0+(m/d)nx = x_0 + (m/d)nx=x0​+(m/d)n,当n=0,1,2,...,d−1n = 0, 1, 2, ..., d -1n=0,1,2,...,d−1时,有ddd个模mmm不同余的解。
  推论:aaa和mmm互素时,因为d=gcd(a,m)=1d = gcd(a, m)=1d=gcd(a,m)=1,所以线性同余方程ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)有唯一的模mmm不同余的解。这个推论在下一节的逆中有应用。
  最后回到求解ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)的问题:首先求逆,然后利用逆求得xxx。

3. 逆

  求解一般形式的同余方程ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m),需要用到逆。

3.1.逆的概念

  给定整数a,且满足gcd(a,m)=1gcd(a, m) = 1gcd(a,m)=1,称 ax≡1(modm)ax\equiv 1(mod\ m)ax≡1(mod m)的一个解为aaa模mmm的逆。记为a−1a^{-1}a−1。
  例如:8x≡1(mod31)8x\equiv 1(mod\ 31)8x≡1(mod 31),有一个解是xxx = 4,4是8模31的逆。所有的解,例如35、66等,都是8模31的逆。
  可以借助丢番图方程理解逆的概念,8x≡1(mod31)8x\equiv 1(mod\ 31)8x≡1(mod 31)即方程8x+31y=18x + 31y = 18x+31y=1,xxx = 4是8模31的逆,4×8-1能整除31。

3.2.求逆

  有多种方法可以求逆。
(1)扩展欧几里得求单个逆
  下面的例题是求逆,即求解同余方程ax≡1(modm)ax\equiv 1(mod\ m)ax≡1(mod m)。


同余方程(求逆) 洛谷 P1082
题目描述:求关于x的同余方程ax≡1(modm)ax\equiv 1(mod\ m)ax≡1(mod m)的最小正整数解。2≤a, m≤2,000,000,000。


  题解:ax≡1(modm)ax\equiv 1(mod\ m)ax≡1(mod m),即丢番图方程ax+my=1ax + my = 1ax+my=1,先用扩展欧几里得求出ax+my=1ax + my = 1ax+my=1的一个特解x0x_0x0​,通解是x=x0+mnx = x_0 + mnx=x0​+mn。然后通过取模操作算最小整数解((x0modm)+m)modm( (x_0 mod\ m) + m) mod\ m((x0​mod m)+m)mod m,因为mmm > 0,可以保证结果是正整数。

long long mod_inverse(long long a, long long m){    //求逆long long x,y;extend_gcd(a,m,x,y);return  (x%m + m) % m;                          //保证返回最小正整数
}
int main(){long long a,m;  cin >> a >>m; cout << mod_inverse(a,m);return 0;
}

(2)费马小定理求单个逆
  费马小定理:设nnn是素数,aaa是正整数且与nnn互质,那么有an−1≡1(modn)a^{n-1}\equiv 1(mod\ n)an−1≡1(mod n)。
  aan−2≡1(modn)a a^{n-2}\equiv 1(mod\ n)aan−2≡1(mod n),那么an−2modna^{n-2} mod\ nan−2mod n就是aaa模nnn的逆。计算需要用到快速幂取模fast_pow(),参考前面章节“大素数的判定”。快速幂取模的复杂度是O(logn)O(log n)O(logn)的。

long long mod_inverse(long long a,long long mod){return fast_pow(a,mod - 2,mod);
}

(3)递推求多个逆
  如果要求1 ~ n内所有的逆,可以用递推。复杂度是O(n)的。


乘法逆元 洛谷 P3811
题目描述:给定 n,pn,pn,p,求 1n1 ~ n1 n 中所有整数在模 ppp 意义下的乘法逆元。
1≤n≤3×1061≤n≤3×10^61≤n≤3×106,n<p<20000528n < p < 20000528n<p<20000528,ppp为质数。
输入:一行两个正整数n、pn、pn、p。
输出:输出nnn行,第iii行表示iii在模ppp下的乘法逆元。


  首先,i=1i=1i=1时逆是1。下面求i>1i > 1i>1时的逆,用递推法。
  设p/i=kp/i = kp/i=k,余数是rrr,即ki+r≡0(modp)k i + r\equiv 0(mod\ p)ki+r≡0(mod p);
  在两边乘i−1r−1i^{-1} r^{-1}i−1r−1,得到kr−1+i−1≡0(modp)k r^{-1} + i^{-1}\equiv 0 (mod\ p)kr−1+i−1≡0(mod p);
  移项得i−1≡−kr−1(modp)i^{-1}\equiv - k r^{-1} (mod\ p)i−1≡−kr−1(mod p) ,即i−1≡−p/ir−1(modp)i^{-1}\equiv - p/i r^{-1} (mod\ p)i−1≡−p/ir−1(mod p) ,i−1≡(p−p/i)r−1(modp)i^{-1}\equiv (p - p/i) r^{-1} (mod\ p)i−1≡(p−p/i)r−1(mod p)。

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

  下面给出一个求逆的例题。


A/B hdu 1576
题目描述:求(A/B)%9973,但由于A很大,我们只给出n (n = A%9973)(我们给定的A必能被B整除,且gcd(B, 9973) = 1)。
输入:第一行是T,表示有T组数据。每组数据有两个数n(0 <= n < 9973)和B(1 <= B <= 10^9)。
输出:对每组数据,输出(A/B)%9973。


题解
  设答案k=(A/B)k = (A/B) % 9973k=(A/B)。
   做以下变换:A/B=k+9973x,A=kB+9973xBA/B = k + 9973 x,A = kB + 9973 x BA/B=k+9973x,A=kB+9973xB;
   把AA % 9973 = nA代入得kBk B % 9973 = nkB,即kB=n+9973yk B = n + 9973ykB=n+9973y;
   两边除nnn得(k/n)B+(−y/n)9973=1(k/n) B + (-y/n) 9973 = 1(k/n)B+(−y/n)9973=1,这是形如ax+by=1ax + by = 1ax+by=1的丢番图方程,即ax≡1(modm)ax\equiv 1(mod\ m)ax≡1(mod m),其中x=k/nx = k/nx=k/n。求解逆xxx,得到k/nk/nk/n,再乘以nnn,就是kkk。

3.3. 用逆求解同余方程

  逆有什么用?如果有aaa模mmm的一个逆,可以用来解如ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)的任何同余方程。
   记a−1a^{-1}a−1是aaa的一个逆,有a−1a≡1(modm)a^{-1}a\equiv 1(mod\ m)a−1a≡1(mod m)。在ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)的两边同时乘以a−1a^{-1}a−1,得到a−1ax≡a−1b(modm)a^{-1}ax\equiv a^{-1}b(mod\ m)a−1ax≡a−1b(mod m),即x≡a−1b(modm)x\equiv a^{-1}b (mod\ m)x≡a−1b(mod m)。
   例如:为了求出8x≡22(mod31)8x\equiv 22 (mod\ 31)8x≡22(mod 31)的解,可以两边乘以4,4是8模31的一个逆,得4∗8x=4∗22(mod31)4 * 8x = 4 * 22 (mod\ 31)4∗8x=4∗22(mod 31),因此x≡88(mod31)≡26(mod31)x\equiv 88 (mod\ 31)\equiv 26 (mod\ 31)x≡88(mod 31)≡26(mod 31)。
   定理:设ppp是素数,正整数aaa是其自身模ppp的逆,当且仅当a≡1(modp)a\equiv 1 (mod\ p)a≡1(mod p)或a≡−1(modp)a\equiv -1 (mod\ p)a≡−1(mod p)。
   证明:若a≡1(modp)a\equiv 1 (mod\ p)a≡1(mod p)或a≡−1(modp)a\equiv -1 (mod\ p)a≡−1(mod p),有a2≡1(modp)a^2\equiv 1 (mod\ p)a2≡1(mod p),所以aaa是其自身模ppp的逆。反过来也成立。

3.4. 逆与除法取模

  逆的一个重要应用是求除法的模。例如在catalan数中,有这样一个需求:求(a/b)modm(a/b) mod\ m(a/b)mod m,即aaa除以bbb,然后对mmm取模。由于这里aaa和bbb都是很大的数,做除法后再取模,会损失精度。用逆可以避免除法计算,设b的逆元是b−1b^{-1}b−1,有:
  (a/b)modm=((a/b)modm)∗((bb−1)modm)=(a/b∗bb−1)modm=(ab−1)modm(a/b) mod\ m = ((a/b) mod\ m)*((bb^{-1}) mod\ m) = (a/b*bb^{-1}) mod\ m = (ab^{-1}) mod\ m(a/b)mod m=((a/b)mod m)∗((bb−1)mod m)=(a/b∗bb−1)mod m=(ab−1)mod m
  经过上述推导,除法的模运算转换成了乘法模运算:(a/b)modm=(ab−1)modm(a/b) mod\ m = (ab^{-1}) mod\ m(a/b)mod m=(ab−1)mod m。
  下面是一个除法取模的例题。


Detachment http://acm.hdu.edu.cn/showproblem.php?pid=5976
题目描述:把一个整数X分成多个整数的和:x = a1 + a2 + …,且ai ≠ aj,使得s = a1 ∗a2 * …最大。
输入:第一行是T,表示测试用例数量,后面有T行,每一行一个整数表示X。1 ≤ T ≤ 10^6, 1 ≤ X ≤ 10^9。
输出:对每个用例,首先计算最大的s,然后输出它对mod = 10^9+7的取模。


题解:如何分解X,才能使积s最大?这是小学奥数题,读者可以自己举例子推理。
  首先,分解的数越多,积s越大。比如X分解为2个数,对比不分解的1个数X:(X - k)(X + k) > X。
  其次,分解的数越接近,积越大。例如分成2个数,对比X/2 - 1、X/2 +1和X/2 - k、X/2 +k,有:(X/2 - 1)(X/2 +1) > (X/2 - k)(X/2 + k)。
  结论是:把X尽量分为更多连续数的和,得到的积s最大。为了分解更多,就从2开始分解:X = 2 + 3 + 4 + 5 + …。从1开始分解不好,因为1对乘积没有贡献。如果还有余数,就把余数拆开,加到其他数上:从后往前加,每个数加上1,这样可以保证每个数都不同。例如17 = 2 + 3 + 4 + 5 + 3,最后有个余数3,把它拆成3个1,加到后面3个数上,得:17 = 2 + 4 + 5 + 6。再例如13 = 2 + 3 + 4 + 4,后面的余数4,拆成4个1,但是前面只有3个数,不够用,多的1再加在最后,得:13 = 3 + 4 + 6。
  分解有两种情况:
   (1)2×3×4×...×(i−1)×(i+1)×...×k×(k+1)2×3×4×...×(i-1)×(i+1)×...×k×(k+1)2×3×4×...×(i−1)×(i+1)×...×k×(k+1),中间少个iii;
  (2)3×4×...×i×(i+1)×...×k×(k+2)3×4×...×i×(i+1)×...×k×(k+2)3×4×...×i×(i+1)×...×k×(k+2),前面少个2,后面多个k+2k+2k+2;
   求s的时候,先计算连续的乘积aaa,然后除以iii,或者除以2乘以k+2。例如第(1)种情况,s=a/is = a/is=a/i,输出的结果是(a/i)%mod(a/i)\ \%\ mod(a/i) % mod。除法取模需要用到逆。本题的模10^9 + 7正好是个素数,所以求逆用扩展欧几里得或费马小定理都行。
   下面给出编码,细节有:
   (1)先预计算出从2开始的前缀和和连续积,用于判断分解到哪个数为止。并用upper_bound()查找x的位置。
   (2)把余数加到后面的数上去,并查找缺少的iii。
   (3)计算结果。用逆计算除法取模。

#include<bits/stdc++.h>
using namespace std;
#define ll long longconst int maxnum = 1e5;             //分解的数不会超过50000个,请自己分析
const int mod = 1e9 + 7;ll sum[maxnum], mul[maxnum];  //前缀和、连续积ll fast_pow(ll x,ll y,int m){       //快速幂取模:x^y mod mll res = 1;while(y) {if(y&1) res*=x, res%=m;x = (x*x) % m;y>>=1;}return res;
}long long mod_inverse(long long a,long long mod){ //费马小定理求逆,或者用扩展欧几里得求逆return fast_pow(a,mod - 2,mod);
}void init(){       //预计算前缀和、连续积sum[1] = 0;  mul[1] = 1;for(int i=2; i<=maxnum; i++){sum[i] = sum[i-1] + i;      //计算前缀和mul[i] = (i*mul[i-1]) % mod;//计算连续积}
} int main(){init();int T; scanf("%d",&T);while(T--){int x; scanf("%d",&x);if( x == 1) {puts("1"); continue;}  //特殊情况int k = upper_bound(sum+1,sum+1+maxnum,x)-sum-1;  //分解成k个数int m = x - sum[k];    //余数ll ans;if(k==m)           ans = mul[k] * mod_inverse(2,mod) %mod * (k+2) % mod; //第2种情况           else            ans = mul[k+1] * mod_inverse(k-m+1,mod) % mod % mod;  //第1种情况printf("%lld\n",ans);}return 0;
}

4. 同余方程组

  根据上一节的讨论,同余方程ax≡b(modm)ax\equiv b (mod\ m)ax≡b(mod m)有解时,即gcd(a,m)gcd(a, m)gcd(a,m) 能整除bbb时,可以解得x≡a′(modm′)x\equiv a' (mod\ m')x≡a′(mod m′),所以这也是同余方程的一般形式。本节讨论同余方程组的求解:
    x≡a1(modm1)x\equiv a_1 (mod\ m_1)x≡a1​(mod m1​)
    x≡a2(modm2)x\equiv a_2 (mod\ m_2)x≡a2​(mod m2​)
    …
    x≡ar(modmr)x\equiv a_r (mod\ m_r)x≡ar​(mod mr​)
  例:有一个数xxx,被3除余2,被5除余3,被7除余2,列成同余方程就是:
    x≡2(mod3)x\equiv 2 (mod\ 3)x≡2(mod 3)
    x≡3(mod5)x\equiv 3 (mod\ 5)x≡3(mod 5)
    x≡2(mod7)x\equiv 2 (mod\ 7)x≡2(mod 7)
  求解结果是:x=23+3×5×7×nx = 23 + 3 × 5 ×7 × nx=23+3×5×7×n,n≥0n ≥ 0n≥0,或者写为x≡23(mod3×5×7)x\equiv 23 (mod\ 3 × 5 × 7)x≡23(mod 3×5×7),xxx的最小正整数解是23。
  本节介绍中国剩余定理和迭代法,前者是用于m1,m2,...,mrm_1,m_2,...,m_rm1​,m2​,...,mr​两两互素情况下的优秀解法,后者是更一般条件下的通用解法。适用中国剩余定理的方程组肯定有解,而迭代法处理的更一般情况,可能是无解的。

4.1. 中国剩余定理

  中国剩余定理1:设m1,m2,...,mrm_1,m_2,...,m_rm1​,m2​,...,mr​是两两互素的正整数,则同余方程组
    x≡a1(modm1)x\equiv a_1 (mod\ m_1)x≡a1​(mod m1​)
    x≡a2(modm2)x\equiv a_2 (mod\ m_2)x≡a2​(mod m2​)
    …
    x≡ar(modmr)x\equiv a_r (mod\ m_r)x≡ar​(mod mr​)
  有整数解,并且模M=m1m2...mrM = m_1m_2...m_rM=m1​m2​...mr​唯一,解为:
  x=(a1M1M1−1+a2M2M2−1+...+arMrMr−1)(modM)x=(a_1M_1M_1^{-1} + a_2M_2M_2^{-1} + ... + a_rM_rM_r^{-1}) (mod\ M)x=(a1​M1​M1−1​+a2​M2​M2−1​+...+ar​Mr​Mr−1​)(mod M)
  其中Mi=M/miM_i = M/m_iMi​=M/mi​,Mi−1M_i^{-1}Mi−1​为MiM_iMi​模mim_imi​的逆元。
   读者可以尝试自己证明。

   例题:解同余方程组x≡2(mod3),x≡3(mod5),x≡2(mod7)x\equiv 2 (mod\ 3),x\equiv 3 (mod\ 5),x\equiv 2 (mod\ 7)x≡2(mod 3),x≡3(mod 5),x≡2(mod 7)。
   解题步骤:
   (1)M=3×5×7=105,M1=105/3=35,M2=105/5=21,M3=105/7=15M = 3 ×5 ×7 = 105,M1 = 105/3 = 35,M2 = 105/5 = 21,M3 = 105/7 = 15M=3×5×7=105,M1=105/3=35,M2=105/5=21,M3=105/7=15。
   (2)求逆:M1−1=2,M2−1=1,M3−1=1M_1^{-1} = 2,M_2^{-1} = 1,M_3^{-1} = 1M1−1​=2,M2−1​=1,M3−1​=1。
   (3)最后计算x:x≡2×35×2+3×21×1+2×15×1≡233≡23(mod105)x:x\equiv 2×35×2 + 3×21×1 + 2×15×1\equiv 233\equiv 23(mod 105)x:x≡2×35×2+3×21×1+2×15×1≡233≡23(mod105)。

4.2. 迭代法

  中国剩余定理的编码很容易,但是它的限制条件是方程组的m1,m2,...,mrm_1,m_2,...,m_rm1​,m2​,...,mr​两两互素。如果不互素,该如何解题呢?这就是迭代法。
  迭代法的思路很简单,就是每次合并两个同余式,逐步合并,直到合并完所有等式,只剩下一个,就得到了答案。
  合并的时候,把同余方程转化为等式更容易操作。这是根据同余的一个性质:若xxx和aaa是整数,则x≡a(modm)x\equiv a (mod\ m)x≡a(mod m)当且仅当存在整数,使得x=a+kmx = a + kmx=a+km。

1、 示例
  以方程组x≡2(mod3),x≡3(mod5),x≡2(mod7)x\equiv 2 (mod\ 3),x\equiv 3 (mod\ 5),x\equiv 2 (mod\ 7)x≡2(mod 3),x≡3(mod 5),x≡2(mod 7)为例,说明合并过程。下面的计算步骤,前3步合并了第1和第2个同余式,后面3步继续合并第3个同余式。
   1)把第1个同余式x≡2(mod3)x\equiv 2 (mod\ 3)x≡2(mod 3)转换为x=2+3tx = 2 + 3tx=2+3t,代入第2个同余式得2+3t≡3(mod5)2 + 3t\equiv 3 (mod\ 5)2+3t≡3(mod 5)。
   2)求解2+3t≡3(mod5)2 + 3t\equiv 3 (mod\ 5)2+3t≡3(mod 5)。
   首先变为3t≡(3−2)(mod5)3t \equiv (3 - 2) (mod\ 5)3t≡(3−2)(mod 5),即3t≡1(mod5)3t \equiv 1 (mod\ 5)3t≡1(mod 5),因为gcd(3,5)gcd(3, 5)gcd(3,5)能整除1,所以有解。
   然后求解3t≡1(mod5)3t \equiv 1 (mod\ 5)3t≡1(mod 5):先求3模5的逆,是2,所以解得t≡2(mod5)t\equiv 2(mod\ 5)t≡2(mod 5),转换为等式t=2+5ut = 2 + 5ut=2+5u。
   3)第1个和第2个同余式合并的结果。把t=2+5ut = 2 + 5ut=2+5u代入x=2+3tx = 2 + 3tx=2+3t得x=8+15ux = 8 + 15ux=8+15u,即x≡8(mod15)x\equiv 8 (mod\ 15)x≡8(mod 15)。
   4)把x=8+15ux = 8 + 15ux=8+15u代入第3个同余式得:8+15u≡2(mod7)8 + 15u \equiv 2 (mod\ 7)8+15u≡2(mod 7)。
   5)求解8+15u≡2(mod7)8 + 15u\equiv 2 (mod\ 7)8+15u≡2(mod 7)。
   首先变为15u≡−6(mod7)15u\equiv -6 (mod\ 7)15u≡−6(mod 7),gcd(15,7)gcd(15, 7)gcd(15,7)能整除−6-6−6,有解。
   然后求15u≡−6(mod7)15u\equiv -6 (mod\ 7)15u≡−6(mod 7):先求15模7的逆,是1,解得u≡1(mod7)u\equiv 1(mod\ 7)u≡1(mod 7),转换为u=1+7vu = 1 + 7vu=1+7v。
   6)得到合并结果。把u=1+7vu = 1 + 7vu=1+7v代入x=8+15ux = 8 + 15ux=8+15u,得x=23+105vx = 23 + 105vx=23+105v,即x≡23(mod105)x\equiv 23(mod\ 105)x≡23(mod 105)。结束。

2、 编码步骤
  下面改用丢番图方程的形式,总结合并两个同余式的编码方法,并以合并上面的前2个等式为例。

步骤 例子
合并两个等式:
x=a1+Xm1x = a_1 + Xm_1x=a1​+Xm1​
x=a2+Ym2x = a_2 + Ym_2x=a2​+Ym2​
x=2+3Xx = 2 + 3Xx=2+3X
x=3+5Yx = 3 + 5Yx=3+5Y
两个等式相等:a1+Xm1=a2+Ym2a_1 + Xm_1 =a_2 + Ym_2a1​+Xm1​=a2​+Ym2​
移项得:Xm1+(−Y)m2=a2−a1Xm_1 + (-Y)m_2 = a_2 - a_1Xm1​+(−Y)m2​=a2​−a1​
2+3X=3+5Y2 + 3X = 3 + 5Y2+3X=3+5Y
3X+5(−Y)=13X + 5(-Y) = 13X+5(−Y)=1
这是形如aX+bY=caX+bY = caX+bY=c的丢番图方程。
下面求解它。先用扩展欧几里得求X0X_0X0​
得:X0=2X_0 =2X0​=2
XXX的通解是X=X0c/d+(b/d)nX = X_0 c / d + (b/d)nX=X0​c/d+(b/d)n
最小值是t=(X0c/d)mod(b/d)t = (X_0 c / d) mod\ (b/d)t=(X0​c/d)mod (b/d)
t=(X0c/d)mod(b/d)t = (X_0 c / d) mod\ (b/d)t=(X0​c/d)mod (b/d)
=(2×1/1)mod(5/1)=2= (2×1/1) mod\ (5/1) = 2=(2×1/1)mod (5/1)=2
把X=tX = tX=t代入x=a1+Xm1x = a_1 + Xm_1x=a1​+Xm1​
求得原等式的一个特解x′x'x′
得x′=2+2×3=8x' = 2 + 2×3 = 8x′=2+2×3=8
合并后的新x=a+Xmx = a + Xmx=a+Xm:
m=m1m2/gcd(m1,m2)m = m_1m_2/gcd(m_1, m_2)m=m1​m2​/gcd(m1​,m2​)
a=x′a = x'a=x′
m=3×5/1=15m = 3×5/1 = 15m=3×5/1=15
a=8a = 8a=8
合并后的新方程是x=8+15Xx = 8 + 15Xx=8+15X
即x≡8(mod15)x\equiv 8 (mod 15)x≡8(mod15)

3、 例程
  下面用一个模板题给出线性同余方程组的代码。


扩展中国剩余定理 洛谷P4777
题目描述:给定n组非负整数ai,mia_i,m_iai​,mi​,求解关于xxx的方程组的最小非负整数解。1≤n≤10^5,1 ≤ ai ≤ 10^12,1≤mi<ai1 ≤ m_i < a_i1≤mi​<ai​,保证所有aia_iai​的最小公倍数不超过 10^18。
    x≡a1(modm1)x\equiv a_1 (mod\ m_1)x≡a1​(mod m1​)
    x≡a2(modm2)x\equiv a_2 (mod\ m_2)x≡a2​(mod m2​)
    …
    x≡an(modmn)x\equiv a_n (mod\ m_n)x≡an​(mod mn​)
输入:第一行是整数n,后面n行,每行两个非负整数ai,mia_i,m_iai​,mi​。
输出:输出一行,为满足条件的非负整数x。


  下面给出的代码2,和前面“编码步骤”基本一致。
  注意代码中的细节,例如求t=(X0c/d)mod(b/d)t = (X_0 c / d) mod\ (b/d)t=(X0​c/d)mod (b/d)的代码是mul(x,c/d,b/d)mul(x, c/d, b/d)mul(x,c/d,b/d),目的是避免越界。其他细节见注释。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100010;
int n;
ll ai[maxn], mi[maxn];
ll mul(ll a,ll b,ll mod){   //乘法取模:a*b % modll res=0;while(b>0){if(b&1) res=(res+a)%mod;a=(a+a)%mod;b>>=1;}return res;
}
ll extend_gcd(ll a,ll b,ll &x,ll &y){   //扩展欧几里得if(b == 0){ x=1; y=0; return a;}ll d = extend_gcd(b,a%b,y,x);y -= a/b * x;return d;
}
ll excrt(){               //求解同余方程组,返回最小正整数解ll x,y;ll m1 = mi[1], a1 = ai[1];      //第1个等式ll ans = 0;for(int i=2;i<=n;i++){          //合并每2个等式       ll a2 = ai[i], m2 = mi[i];  // 第2个等式 //合并为:aX + bY = c      ll a = m1, b = m2, c = (a2 - a1%m2 + m2) % m2;//下面求解 aX + bY = cll d = extend_gcd(a,b,x,y);  //用扩展欧几里得求x0if(c%d != 0) return -1;      //无解x = mul(x,c/d,b/d);          //aX + bY = c 的特解t,最小值         ans = a1 + x* m1;            //代回原第1个等式,求得特解x'm1 = m2/d*m1;                //先除再乘,避免越界。合并后的新m1ans = (ans%m1 + m1) % m1;    //最小正整数解a1 = ans;                    //合并后的新a1}return ans;
}int main(){scanf("%d", &n);for(int i=1;i<=n;++i)  scanf("%lld%lld",&mi[i],&ai[i]);printf("%lld",excrt());return 0;
}

  1. 公元3世纪《孙子算经》中有一个问题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?答曰:二十三。”1247年,秦九韶在《数学九章》中给出了求解的一般方法“大衍求一术”,被称为“中国剩余定理(Chinese Remainder Theorem)”。秦九韶是全能型的天才,在多个领域有建树。 ↩︎

  2. 参考:https://www.luogu.com.cn/problem/solution/P4777 ↩︎

同余 --算法竞赛专题解析(22):数论相关推荐

  1. 二分法、三分法 --算法竞赛专题解析(1)

    本系列文章将于2021年整理出版,书名<算法竞赛专题解析>. 前驱教材:<算法竞赛入门到进阶> 清华大学出版社 2019.8 网购:京东 当当      作者签名书 如有建议, ...

  2. 四边形不等式优化 --算法竞赛专题解析(10)

    本系列文章将于2021年整理出版,书名<算法竞赛专题解析>. 前驱教材:<算法竞赛入门到进阶> 清华大学出版社 2019.8 网购:京东 当当      作者签名书 如有建议, ...

  3. 树形DP --算法竞赛专题解析(17)

    本系列文章将于2021年整理出版,书名<算法竞赛专题解析>. 前驱教材:<算法竞赛入门到进阶> 清华大学出版社 网购:京东 当当      想要一本作者签名书?点我 如有建议, ...

  4. 尺取法 --算法竞赛专题解析(2)

    本系列文章将于2021年整理出版,书名<算法竞赛专题解析>. 前驱教材:<算法竞赛入门到进阶> 清华大学出版社 2019.8 网购:京东 当当      作者签名书 如有建议, ...

  5. 线性丢番图方程 --算法竞赛专题解析(21):数论

    本系列文章将于2021年整理出版.前驱教材:<算法竞赛入门到进阶> 清华大学出版社 网购:京东 当当   作者签名书:点我 公众号同步:算法专辑    暑假福利:胡说三国 有建议请加QQ ...

  6. 线段树 --算法竞赛专题解析(24)

    本系列文章将于2021年整理出版.前驱教材:<算法竞赛入门到进阶> 清华大学出版社 网购:京东 当当   作者签名书:点我 有建议请加QQ 群:567554289 文章目录 1. 线段树概 ...

  7. a*算法迷宫 c++_算法竞赛专题解析(12):搜索基础

    搜索 搜索,就是查找解空间,它是"暴力法"算法思想的具体实现. 文章目录: 01 搜索简介 02 搜索算法的基本思路 03 BFS的性质和代码实现 04 DFS的常见操作和代码实现 ...

  8. 《算法竞赛进阶指南》数论篇

    <算法竞赛进阶指南>数论篇(1)-最大公约数,素数筛,欧拉函数,同余,欧拉定理,BSGS <算法竞赛进阶指南>数论篇(1)-最大公约数,素数筛,欧拉函数,同余,欧拉定理,BSG ...

  9. 韩信点兵(hanxin)--算法竞赛经典习题2-2:相传韩信才智过人,从不清点自己军队的人数,只要让士兵先后以三人一排、五人一排、七人一排地变换队形,他每次只要掠一眼队伍的排尾就知道总数(C++实现)

    韩信点兵(hanxin)–算法竞赛经典习题2-2:相传韩信才智过人,从不清点自己军队的人数,只要让士兵先后以三人一排.五人一排.七人一排地变换队形,他每次只要掠一眼队伍的排尾就知道总数. (C++实现 ...

最新文章

  1. 使用jQuery来创建Silverlight
  2. android摄像头方向与屏方向,Android通过ExifInterface判断Camera图片方向的方法
  3. java data jpa_Spring Data JPA(一)简介
  4. 静态库的冲突 duplicate symbol
  5. MySQL5.5.27使用Restore From SQL Dump功能导入数据库表中出现Row size too large
  6. linux 11Gasm启动crs,11GR2重建CRS
  7. Perl+批处理实现半自动批量生成动态通讯组
  8. Oracle高级教程
  9. 100行代码搞定Python做OCR识别身份证,文字各种字体!
  10. 如何清理C盘空间垃圾?一键清理系统垃圾保障Windows快速运行
  11. 现代天线设计——学习笔记(一)
  12. 是真的吗?蚂蚁的LDC架构,到底是干嘛的,真的那么牛吗
  13. 46.把数字翻译成字符串
  14. everedit 格式化json_Visual studio code (VS code)
  15. 【快速幂取模】NOI 7833:幂的末尾
  16. 不可随便给一个人说晚安『你知道它的真正意义吗?不想以后后悔就进来看看吧』
  17. saiku 部署运行
  18. Java Scanner的hasNext()方法
  19. 拥有谷歌100万股原始股,奥尼尔每年能够得到多少分红?
  20. Windows 10 配置OpenGL ES 3.0 环境

热门文章

  1. unity开发 斗地主算法—提示AI(提示出牌)
  2. html 下拉组件被下面的组件挡住,select挡住div的5种解决方法
  3. el-table滚动条被挡住的问题
  4. 动词的时态---将来时
  5. IntelliJ IDEA 新版 UI 中的项目导航改进
  6. mdpi的手机_APP设计尺寸
  7. 结构、流体、热分析、多物理场耦合、电磁仿真硬件配置推荐2018
  8. 本地直播平台的搭建—四种方式(转载)
  9. 大型网站架构之架构演变
  10. 【运维心得】网络ID与网络IP的区别你知道吗?