扩展中国剩余定理

欢迎访问个人博客

简介

  对于一元线性同余方程组 { x ≡ a 1 (   m o d   m 1 ) x ≡ a 2 (   m o d   m 2 ) ⋮ x ≡ a n (   m o d   m n ) \left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right) \\ \vdots \\ x \equiv a_{n}\left(\bmod m_{n}\right) \end{array}\right. xa1(modm1)xa2(modm2)xan(modmn),中国剩余定理的适用条件是 m 1 , m 2 , ⋯   , m n m_1,m_2,\cdots,m_n m1,m2,,mn 两两互质。
  扩展中国剩余定理适用于 m 1 , m 2 , ⋯   , m n m_1,m_2,\cdots,m_n m1,m2,,mn 不是两两互质的情况。

求解

  考虑前方程组前两个方程, { x ≡ a 1 (   m o d   m 1 ) x ≡ a 2 (   m o d   m 2 ) \left\{\begin{array}{c} x \equiv a_{1}\left(\bmod m_{1}\right) \\ x \equiv a_{2}\left(\bmod m_{2}\right)\end{array}\right. {xa1(modm1)xa2(modm2),一定存在整数 p , q p,q p,q,使得 x = m 1 p + a 1 = m 2 q + a 2 x=m_1p+a_1=m_2q+a_2 x=m1p+a1=m2q+a2,即 m 1 p + a 1 = m 2 q + a 2 m_1p+a_1=m_2q+a_2 m1p+a1=m2q+a2,移项后得到 m 1 p − m 2 q = a 2 − a 1 m_1p-m_2q=a_2-a_1 m1pm2q=a2a1。结合方程组前二式,得到一个裴蜀等式,由裴蜀定理,当且仅当 gcd ⁡ ( m 1 , m 2 ) ∣ ( a 2 − a 1 ) \gcd(m_1,m_2)\mid(a_2-a_1) gcd(m1,m2)(a2a1) 时方程有解。
  假设得到的裴蜀等式有解, p 0 p_0 p0 为方程 m 1 x + m 2 y = gcd ⁡ ( m 1 , m 2 ) m_1x+m_2y=\gcd(m_1,m_2) m1x+m2y=gcd(m1,m2) 的特解,那么 p p p 的通解为 p = p= p= a 2 − a 1 gcd ⁡ ( m 1 , m 2 ) p 0 \frac{a_2-a_1}{\gcd(m_1,m_2)}p_0 gcd(m1,m2)a2a1p0 + k m 2 gcd ⁡ ( m 1 , m 2 ) ( k ∈ Z ) +k\frac{m_2}{\gcd(m_1,m_2)}(k\in Z) +kgcd(m1,m2)m2(kZ)。所以 x = m 1 p + a 1 = a 2 − a 1 gcd ⁡ ( m 1 , m 2 ) m 1 p 0 + a 1 + k  lcm ( m 1 , m 2 ) x=m_1p+a_1=\frac{a_2-a_1}{\gcd(m_1,m_2)}m_1p_0+a_1+k\ \text{lcm}(m_1,m_2) x=m1p+a1=gcd(m1,m2)a2a1m1p0+a1+k lcm(m1,m2)。合并后的通解为 x ≡ a 2 − a 1 gcd ⁡ ( m 1 , m 2 ) m 1 p 0 + a 1 ( m o d lcm ( m 1 , m 2 ) ) x\equiv \frac{a_2-a_1}{\gcd(m_1,m_2)}m_1p_0+a_1\pmod{\text{lcm}(m_1,m_2)} xgcd(m1,m2)a2a1m1p0+a1(modlcm(m1,m2))
  综上所述,使用 n n n 次扩展欧几里得算法,不断合并同余式,最后的余项即为答案。

模板

#include<iostream>
#include<cstdio>
#define ll long long
#define N 100003
using namespace std;
int n;
ll a[N],m[N];
//---------------------------------------
//扩展欧几里得算法
//用于将两个线性同余方程合并
inline ll ex_gcd(ll a,ll b,ll &x,ll &y)
{
	if(!b)
	{
		x=1;
		y=0;
		return a;
	}
	ll g=ex_gcd(b,a%b,x,y);
	ll temp=x;
	x=y;
	y=temp-a/b*y;
	return g;
}
//---------------------------------------
//---------------------------------------
//扩展中国剩余定理
//不断合并两个线性同余方程
//最后的A即为最小正整数解
void ex_CRT()
{
	ll M=m[1],A=a[1];
	int i;
	for(i=2;i<=n;i++)
	{
		ll p,q;
		ll g=ex_gcd(M,m[i],p,q);
		if((a[i]-A)%g) //无解
		{
			puts("-1");
			return;
		}
		p=(a[i]-A)/g*p;
		//将p取为正解
		p=(p%(m[i]/g)+m[i]/g)%(m[i]/g);
		//合并后,方程未知数x与A模M同余
		A=M*p+A;//m1p+a1
		M=M/g*m[i];//lcm(m1,m2)
		//对A取模
		A=(A%M+M)%M;
	}
	cout<<A<<endl;
}
//----------------------------------------
int main()
{
	cin>>n;
	int i;
	for(i=1;i<=n;i++) scanf("%lld%lld",m+i,a+i);
	ex_CRT();
	return 0;
}

洛谷P4777 【模板】扩展中国剩余定理(EXCRT) ↬ \looparrowright

题目描述

  给定 n n n 组非负整数 a i , b i a_i, b_i ai,bi,求解关于 x x x 的方程组的最小非负整数解。
{ x ≡ b 1 (   m o d   a 1 ) x ≡ b 2 (   m o d   a 2 ) ⋮ x ≡ b n (   m o d   a n ) \left\{\begin{array}{c} x \equiv b_{1}\left(\bmod a_{1}\right) \\ x \equiv b_{2}\left(\bmod a_{2}\right) \\ \vdots \\ x \equiv b_{n}\left(\bmod a_{n}\right) \end{array}\right. xb1(moda1)xb2(moda2)xbn(modan)

输入格式

  输入第一行包含整数 n n n
  接下来 n n n 行,每行两个非负整数 a i , b i a_i, b_i ai,bi

输出格式

  输出一行,为满足条件的最小非负整数 x x x

输入输出样例

输入 #1

3
11 6
25 9
33 17

输出 #1

809

说明/提示

  对于 100 % 100 \% 100% 的数据, 1 ≤ n ≤ 10 5 1 \le n \le {10}^5 1n105 1 ≤ a i ≤ 10 12 1 \le a_i \le {10}^{12} 1ai1012 0 ≤ b i < a i 0 \le b_i < a_i 0bi<ai,保证所有 a i a_i ai 的最小公倍数不超过 10 18 {10}^{18} 1018
  请注意程序运行过程中进行乘法运算时结果可能有溢出的风险。
  数据保证有解。

分析

  扩展欧几里得算法模板题,乘法运算时有可能爆 long long \text{long long} long long,需要使用 __ int128 \text{int128} int128。__ int128 \text{int128} int128 最大可达 2 128 ≈ 3.4 × 1 0 39 2^{128}\approx 3.4\times 10^{39} 21283.4×1039

代码

#include<cstdio>
#include<iostream>
#define ll long long
#define BigInt __int128
#define N 100002
using namespace std;
int n;
ll a[N],b[N];
inline BigInt ex_gcd(BigInt a,BigInt b,BigInt &x,BigInt &y)
{
	if(!b)
	{
		x=1;
		y=0;
		return a;
	}
	BigInt g=ex_gcd(b,a%b,x,y);
	BigInt temp=x;
	x=y;
	y=temp-a/b*y;
	return g;
}
void ex_CRT()
{
	BigInt A=a[1],B=b[1];
	int i;
	for(i=2;i<=n;i++)
	{
		BigInt p,q;
		BigInt g=ex_gcd(A,a[i],p,q);
		if((b[i]-B)%g) 
		{
			puts("-1");
			return;
		}
		p=(b[i]-B)/g*p;
		p=(p%(a[i]/g)+a[i]/g)%(a[i]/g);
		B=A*p+B;
		A=A/g*a[i];
		B=(B%A+A)%A;
	}
	cout<<(ll)B<<endl;//转为long long输出
}
int main()
{
	cin>>n;
	int i;
	for(i=1;i<=n;i++) scanf("%lld%lld",a+i,b+i);
	ex_CRT();
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值