【学习笔记】中国剩余定理及其扩展

1. 中国剩余定理(CRT)

1.1 问题引入

  • 给定同余方程组,形如

{ x ≡ a 1 ( m o d   p 1 ) x ≡ a 2 ( m o d   p 2 )          … x ≡ a n ( m o d   p n ) \begin{cases} x\equiv a_1 (mod~p_1) \\ x\equiv a_2 (mod~p_2) \\ ~~~~ ~ ~~ ~\dots \\ x\equiv a_n (mod~p_n) \\ \end{cases} xa1(mod p1)xa2(mod p2)        xan(mod pn)

  • 其中 p i p_i pi 两两互质。

1.2 方程组的解性

  • 我们记 M = ∏ i = 1 n p i M=\prod_{i=1}^np_i M=i=1npi m i = m p i m_i=\frac{m}{p_i} mi=pim
  • 我们假设已经得到一个解 x 0 x_0 x0,则 x 0 + k M ( k ∈ Z ) x_0+kM(k\in \mathbb Z) x0+kM(kZ) 显然都是方程组的解。
  • 即所有的解构成关于 M M M 的一个剩余类。
  • 因此我们只需要求出 [ 0 , M ) [0,M) [0,M) 中的整数解即可。
  • 中国剩余定理给出结论,这个方程在 [ 0 , M ) [0,M) [0,M) 中存在唯一解

1.3 方程组的解法

  • 我们先考虑如何求出解。
  • 我们考虑 n n n 个方程组,第 i i i 个方程组为

{ x i ≡ 0 ( m o d   p 1 ) x i ≡ 0 ( m o d   p 2 )          … x i ≡ a i ( m o d   p i )          … x i ≡ 0 ( m o d   p n − 1 ) x i ≡ 0 ( m o d   p n ) \begin{cases} x_i\equiv 0 (mod~p_1) \\ x_i\equiv 0 (mod~p_2) \\ ~~~~ ~ ~~ ~\dots \\ x_i \equiv a_i (mod~p_i)\\ ~~~~~ ~~ ~ \dots \\ x_i\equiv 0 (mod~p_{n-1}) \\ x_i\equiv 0 (mod~p_n) \\ \end{cases} xi0(mod p1)xi0(mod p2)        xiai(mod pi)        xi0(mod pn1)xi0(mod pn)

  • 那么 ∑ i = 1 n x i \sum_{i=1}^nx_i i=1nxi 就为一个解。
  • 只需要对 M M M 取模即得到 [ 0 , n ) [0,n) [0,n) 内的特解。
  • 考虑 x i x_i xi 怎么求。
  • 我们再考虑一个方程组

{ y i ≡ 0 ( m o d   p 1 ) y i ≡ 0 ( m o d   p 2 )          … y i ≡ 1 ( m o d   p i )          … y i ≡ 0 ( m o d   p n − 1 ) y i ≡ 0 ( m o d   p n ) \begin{cases} y_i\equiv 0 (mod~p_1) \\ y_i\equiv 0 (mod~p_2) \\ ~~~~ ~ ~~ ~\dots \\ y_i \equiv 1 (mod~p_i)\\ ~~~~~ ~~ ~ \dots \\ y_i\equiv 0 (mod~p_{n-1}) \\ y_i\equiv 0 (mod~p_n) \\ \end{cases} yi0(mod p1)yi0(mod p2)        yi1(mod pi)        yi0(mod pn1)yi0(mod pn)

  • 那么我们可以令 x i = a i y i x_i=a_iy_i xi=aiyi
  • 因为 p j ∣ y i ( j ≠ i ) p_j|y_i(j\neq i) pjyi(j̸=i),而 p i p_i pi 之间两两互质,所以 m i m_i mi 在模 p i p_i pi 意义下存在逆元,所以我们可以直接令 y i = m i × m i − 1 y_i=m_i\times m_i^{-1} yi=mi×mi1 m i − 1 m_i^{-1} mi1 表示逆元。
  • 逆元可以视情况用 e x g c d exgcd exgcd 或者费马小定理求。
  • 所以 x i = m i × m i − 1 × a i x_i=m_i\times m_i^{-1}\times a_i xi=mi×mi1×ai
  • 所以 a n s = ∑ i = 1 n m i × m i − 1 × a i ans=\sum_{i=1}^nm_i\times m_i^{-1}\times a_i ans=i=1nmi×mi1×ai,为了防止溢出,我们只需要在模 M M M 意义下做运算即可。
  • 注意在 C R T CRT CRT e x C R T exCRT exCRT 中,有时乘法运算可能会溢出,关于如何处理这类问题参见 【小技巧】O(1)快速乘 - changle_cyx

1.4 解在模 M M M 意义下唯一

  • 接下来我们证明这个解在模 M M M 意义下唯一。
  • 即证明不存在两个解 x 0 , x 1 x_0,x_1 x0,x1, 使得 M ∤ x 1 − x 0 M\nmid x_1-x_0 Mx1x0
  • 反证法:
    • x 0 x_0 x0 表示原方程组的一个特解,存在另解 x 0 + d x_0+d x0+d,使得 M ∤ d M\nmid d Md
    • ∃ i ∈ [ 1 , n ] , p i ∤ d \exist i\in [1,n],p_i\nmid d i[1,n]pid,即 d ̸ ≡ 0 ( m o d   p i ) d\not \equiv 0(mod~p_i) d̸0(mod pi)
    • x 0 x_0 x0 是原方程组的解可得 a n s 0 ≡ a i ( m o d   p i ) ans_0\equiv a_i(mod~p_i) ans0ai(mod pi)
    • 所以 x 0 + d ̸ ≡ a i ( m o d   p i ) x_0+d\not \equiv a_i(mod~p_i) x0+d̸ai(mod pi) x 0 + d x_0+d x0+d 不是原方程组的解,与假设矛盾。
    • 证毕。
  • 所以我们得到了中国剩余定理。
  • 中国剩余定理中的这个唯一性应用广泛,在第3部分中举两个例子说明。

2. 扩展中国剩余定理(exCRT)

2.1 问题引入

  • 中国剩余定理求解的是模数两两互质的方程组。
  • 那么对于模数不满足两两互质的方程组该如何求解呢?
  • 即求解
    { x ≡ a 1 ( m o d   p 1 ) x ≡ a 2 ( m o d   p 2 )          … x ≡ a n ( m o d   p n ) \begin{cases} x\equiv a_1 (mod~p_1) \\ x\equiv a_2 (mod~p_2) \\ ~~~~ ~ ~~ ~\dots \\ x\equiv a_n (mod~p_n) \\ \end{cases} xa1(mod p1)xa2(mod p2)        xan(mod pn)
  • 其中 p i p_i pi 没有特殊约束。

2.2 方程组的解性

  • 方程组不一定有解,我们可以在求解过程中顺便判断。
  • 我们记 m i = l c m ( p 1 , p 2 , … , p i ) m_i=lcm(p_1,p_2,\dots,p_i) mi=lcm(p1,p2,,pi) M = l c m ( p 1 , p 2 , … , p n ) M=lcm(p_1,p_2,\dots,p_n) M=lcm(p1,p2,,pn)
  • 假设我们得到一个特解 x 0 x_0 x0,那么 x 0 + k M ( k ∈ Z ) x_0+kM(k\in \mathbb Z) x0+kM(kZ) 都是方程组的解。
  • 解出来的 x x x 同样是一个剩余类。
  • 类似1.4的证明,我们仍然可以得到若方程组有解,则方程组的解在模 M M M 意义下唯一。

2.3 方程组的解法

  • 我们不能像互质的情况那样做的原因就是,因为不互质,所以我们需要的乘法逆元可能不存在。
  • 我们考虑从前往后,一个一个方程地合并。
  • 假设我们解到第 i i i 个方程,我们就令 x i x_i xi 表示模 m i m_i mi 意义下的唯一解。
  • 对于第 1 1 1 个方程,我们直接令 x 1 = a 1 x_1=a_1 x1=a1
  • 对于第 i ( i > 1 ) i(i>1) i(i>1) 个方程,我们知道前 i − 1 i-1 i1 个方程的 通解 x i − 1 + k m i − 1 ( k ∈ Z ) x_{i-1}+km_{i-1}(k\in \mathbb Z) xi1+kmi1(kZ)
  • 那么现在我们加上第 i i i 个方程的限制 x ≡ a i ( m o d   p i ) x\equiv a_i(mod~p_i) xai(mod pi)
  • 那么前 i i i 个方程联立后等价于方程

x i − 1 + k m i − 1 ≡ a i ( m o d   p i ) x_{i-1}+km_{i-1}\equiv a_i(mod~p_i) xi1+kmi1ai(mod pi)

  • 写成不定方程的形式即为

m i − 1 k + p i y = a i − x i − 1 m_{i-1}k+p_iy=a_i-x_{i-1} mi1k+piy=aixi1

  • 然后用扩展欧几里得算法( e x g c d exgcd exgcd)解一下这个方程。
  • 若存在某个 i i i,使得这个方程无解,即 ( m i − 1 , p i ) ∤ a i − x i − 1 (m_{i-1},p_i)\nmid a_i-x_{i-1} (mi1,pi)aixi1,意味着整个方程组也无解。
  • 否则令解出的特解 k k k 取到模 p i ( m i − 1 , p i ) \frac{p_i}{(m_{i-1},p_i)} (mi1,pi)pi 意义下的唯一解,即令 k k k p i ( m i − 1 , p i ) \frac{p_i}{(m_{i-1},p_i)} (mi1,pi)pi 取模。
  • 然后得到 x i = x i − 1 + m i − 1 k x_i=x_{i-1}+m_{i-1}k xi=xi1+mi1k,然后也令 x i x_i xi m i m_i mi 取模即可。
  • 最后 x n x_n xn 即为答案。
  • 模板题:洛谷P4777 - 扩展中国剩余定理(EXCRT)
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

template <class T>
inline void read(T &x)
{
	static char ch; 
	while (!isdigit(ch = getchar())); 
	x = ch - '0'; 
	while (isdigit(ch = getchar()))
		x = x * 10 + ch - '0'; 
}

typedef long long s64; 

const int MaxN = 1e5 + 5; 

int n; 
s64 p[MaxN], a[MaxN], pre_lcm[MaxN];

inline s64 qmul(s64 b, s64 p, const s64 &mod)
{
	b = (b % mod + mod) % mod; 
	p = (p % mod + mod) % mod; 
	s64 res = 0; 
	for (; p; p >>= 1, (b = b + b) > mod ? (b -= mod) : 0)
		if (p & 1)
		{
			res = res + b; 
			if (res >= mod)
				res -= mod; 
		}
	return res; 
}

inline s64 ex_gcd(const s64 &a, const s64 &b, s64 &x, s64 &y)
{
	if (!b)
		return x = 1, y = 0, a; 
	s64 res = ex_gcd(b, a % b, y, x); 
	y -= a / b * x; 
	return res; 
}

inline s64 solve_equ(const s64 &a, const s64 &b, const s64 &c)
{
	s64 x, y; 
	s64 d = ex_gcd(a, b, x, y); 
	return qmul(x, c / d, b / d); 
}

int main()
{
	read(n); 
	for (int i = 1; i <= n; ++i)
		read(p[i]), read(a[i]); 
	pre_lcm[1] = p[1]; 
	for (int i = 2; i <= n; ++i)
		pre_lcm[i] = p[i] / std::__gcd(pre_lcm[i - 1], p[i]) * pre_lcm[i - 1]; 
	
	s64 x = a[1]; 
	for (int i = 2; i <= n; ++i)
	{
		s64 k = solve_equ(pre_lcm[i - 1], p[i], a[i] - x); 
		x = x + qmul(pre_lcm[i - 1], k, pre_lcm[i]); 
		if (x >= pre_lcm[i])
			x -= pre_lcm[i]; 
	}
	
	std::cout << x << std::endl; 
	
	return 0; 
}

3. 中国剩余定理的简单应用

  • 我们举两个十分十分十分简单的例子(dalao勿d)。

3.1 欧拉函数是积性函数的证明

  • 欧拉函数 φ ( n ) = ∑ i = 1 n [ i ⊥ n ] \varphi(n)=\sum_{i=1}^n[i\perp n] φ(n)=i=1n[in],即 1 1 1 n n n 中与 n n n 互质的数的个数。

  • 欧拉函数是积性函数。

  • p ⊥ q p\perp q pq,则 φ ( p q ) = φ ( p ) φ ( q ) \varphi(pq)=\varphi(p)\varphi(q) φ(pq)=φ(p)φ(q)

  • 下面从中国剩余定理的角度给出证明:

    • 我们设 A A A 表示 1 1 1 p p p 中与 p p p 互质的数组成的集合, B B B 表示 1 1 1 q q q 中与 q q q 互质的数组成的集合, C C C 表示 1 1 1 p q pq pq 中与 p q pq pq 互质的数的集合。
    • 因为 p ⊥ q p\perp q pq,所以 i ⊥ p q ⇔ i ⊥ p 且 i ⊥ q i\perp pq\Leftrightarrow i\perp p且i\perp q ipqipiq
    • 根据辗转相减法,我们知道 ( n , m ) = ( n , m + k n ) , k ∈ Z (n,m)=(n,m+kn),k\in \mathbb Z (n,m)=(n,m+kn)kZ
    • 结合上述结论,我们可以得到

    C = { x ∣ { x ≡ a ( m o d   p ) x ≡ b ( m o d   q ) , a ∈ A , b ∈ B } C=\{x|\begin{cases}x\equiv a(mod~p)\\ x\equiv b(mod~q)\end{cases},a\in A,b\in B\} C={x{xa(mod p)xb(mod q)aAbB}

    • 根据中国剩余定理,我们对于每个 a ∈ A , b ∈ B a\in A,b\in B aAbB,我们都能得到同余方程组
      { x ≡ a ( m o d   p ) x ≡ b ( m o d   q ) \begin{cases}x\equiv a(mod~p)\\ x\equiv b(mod~q)\end{cases} {xa(mod p)xb(mod q)
    • 的唯一解 x x x,并且显然在 [ 1 , p q ] [1,pq] [1,pq] 中,不会存在一个 x x x,使得 x x x 是两个不同的同余方程组的解。
    • 因此我们可以在 A × B A\times B A×B(笛卡尔乘积)和 C C C 之间建立双射。
    • 所以集合 C C C 的元素个数等于集合 A A A 和集合 B B B 的元素个数的乘积。
    • 证毕。

3.2 求解特殊模数的数论题

  • 这里举一道很经典的题目:BZOJ1951 [SDOI2010]古代猪文
  • 题目大意:求 G ∑ d ∣ n C n d m o d &ThinSpace;&ThinSpace; p ( p = 999911659 , n , G ≤ 1 0 9 ) G^{\sum_{d|n}C_{n}^{d}}\mod p(p=999911659,n,G\le10^9) GdnCndmodpp=999911659n,G109
  • 我们知道 p p p 是质数。
  • 大家肯定会问,大部分数论题模数不都是质数吗,这有啥特殊的??
  • 但是你发现这个 G G G 的指数很大,不能直接求出精确值。
  • 我们知道欧拉定理 a b ≡ a b % φ ( p ) ( m o d   p ) , a ⊥ p a^b\equiv a^{b\%\varphi(p)}(mod~p),a\perp p abab%φ(p)(mod p)ap
  • p ∣ G p|G pG 时,答案为 0 0 0
  • 否则有 G ⊥ p G\perp p Gp,我们可以用欧拉定理解决。
  • 也就是我们的指数只需要取 ∑ d ∣ n C n d m o d &ThinSpace;&ThinSpace; φ ( p ) \sum_{d|n}C_{n}^{d}\mod \varphi(p) dnCndmodφ(p),即 ∑ d ∣ n C n d m o d &ThinSpace;&ThinSpace; 999911658 \sum_{d|n}C_{n}^{d}\mod 999911658 dnCndmod999911658
  • 现在问题是,我们如果直接用 L u c a s Lucas Lucas 定理求解,不满足模数是质数的条件。
  • 有人说:我会扩展Lucas!! (但是作者还不会)
  • 但是这题完全没有必要用什么扩展 L u c a s Lucas Lucas
  • 因为 999911658 = 2 × 3 × 4679 × 35617 999911658=2\times3\times4679\times35617 999911658=2×3×4679×35617,即这个数的每个质因子的次数都为 1 1 1
  • 所以我们发现我们只需要求出 ∑ d ∣ n C n d \sum_{d|n}C_{n}^{d} dnCnd 对这四个数取模的结果后,用 C R T CRT CRT 合并即可( C R T CRT CRT 告诉我们,求出这四个答案,列出同余方程组,可以得到模 M M M 意义下的唯一解)。
  • 然后就转化为模数为质数的问题,直接用 L u c a s Lucas Lucas 定理解决就好了。
  • L u c a s Lucas Lucas 定理( p p p 为质数): ( n m ) = ( n / p m / p ) ( n % p m % p ) \binom{n}{m}=\binom{n/p}{m/p}\binom{n\%p}{m\% p} (mn)=(m/pn/p)(m%pn%p)
#include <bits/stdc++.h>
//999911658=2*3*4679*35617

const int mod = 999911659; 

const int p[5] = {0, 2, 3, 4679, 35617}; 
const int MaxN = 4e4 + 5; 

int n, G; 
int a[5], fac[MaxN], inv[MaxN]; 

inline void add(int &x, const int &y, const int &mod)
{
	x += y; 
	x >= mod ? x -= mod : 0; 
}

inline int qpow(int a, int b, const int &mod)
{
	int res = 1; 
	for (; b; b >>= 1, a = 1LL * a * a % mod)
		if (b & 1)
			res = 1LL * res * a % mod; 
	return res; 
}

inline int lucas(const int &n, const int &m, const int &mod)
{
	if (n < m) return 0; 
	if (n < mod && m < mod) return 1LL * fac[n] * inv[m] % mod * inv[n - m] % mod; 
	
	return 1LL * lucas(n % mod, m % mod, mod) * lucas(n / mod, m / mod, mod) % mod; 
}

inline int solve(const int &p)
{
	fac[0] = 1; 
	for (int i = 1; i < p; ++i)
		fac[i] = 1LL * fac[i - 1] * i % p; 
	inv[p - 1] = qpow(fac[p - 1], p - 2, p); 
	for (int i = p - 2; i >= 0; --i)
		inv[i] = 1LL * inv[i + 1] * (i + 1) % p; 
	
	int res = 0; 
	for (int i = 1; i * i <= n; ++i)
		if (n % i == 0)
		{
			add(res, lucas(n, i, p), p); 
			if (i * i != n)
				add(res, lucas(n, n / i, p), p); 
		}
	return res; 
}

int main()
{
	scanf("%d%d", &n, &G); 
	if (G == mod)
	{
		puts("0"); 
		return 0; 
	}
	
	for (int i = 1; i <= 4; ++i)
		a[i] = solve(p[i]); 
	
	int lcm = mod - 1, ans = 0; 
	for (int i = 1; i <= 4; ++i)
	{
		int x = lcm / p[i]; 
		add(ans, 1LL * x * qpow(x, p[i] - 2, p[i]) % lcm * a[i] % lcm, lcm); 
	}
	printf("%d\n", qpow(G, ans, mod)); 
	
	return 0; 
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值