中国剩余定理

{xa1 (mod m1)xa2 (mod m2)xar (mod mr)

问题

对于以上同余方程组,求解改同余方程组的未知数 x


中国剩余定理(CRT)

m1,m2,...,mr两两互质的正整数,则同余方程组有整数解。

  1. 所有模数的积 M=m1m2mr
  2. 计算第 i 个方程的 ci=Mmi
  3. 计算 cimi 意义下的逆元 ci1
  4. 结果 x=i=1nricici1 (mod M)

由于一般题目中的 m1,m2,...,mr 不会交代是否两两互质,所以不能用中国剩余定理直接求解。

这是需要扩展中国剩余定理求解 。


扩展中国剩余定理(EXCRT)

问题改为求 x 的最小非负整数解。

对于前两个方程 xa1 (mod m1)xa2 (mod m2)

可以转化为丢番图方程 x=m1p+a1=m2q+a2

所以,m1pm2q=a2a1


由裴蜀定理

(m1,m2)(a2a1) 时,无解。

(m1,m2)(a2a1) 时,有解。


由扩欧算法

exgcd 函数返回值 p,q,可以得到特解 p0=p×a2a1(m1,m2),q0=q×a2a1(m1,m2)

通解为 P=p0+m2(m1,m2)Q=q0m1(m1,m2)

所以 x=m1P+a1=m1m2(m1,m2)×k+m1p0+a1

数论中我们记 ab 的最小公倍数为 [a,b]

前两个方程等价合并为一个方程 xa (mod m)

其中 a=m1p0+a1m=lcm(m1,m2)=[m1,m2]

所以 n 个同余方程只要合并 n1 次,即可求解。



[AcWing204] 表达整数的奇怪方式

题目描述

给定 2n 个整数 a1,a2,,anm1,m2,,mn,求一个最小的非负整数 x,满足 i[1,n],xmi(mod ai)

输入格式

1 行包含整数 n

2n+1 行:第 i+1 行包含两个整数 aimi,数之间用空格隔开。

输出格式

输出最小非负整数 x,如果 x 不存在,则输出 1

数据范围

1ai2311,
0mi<ai
1n25
所有 mi 的最小公倍数在 64 位有符号整数范围内。

输入样例:

2
8 7
11 9

输出样例:

31

算法

扩展中国剩余定理模版题。

C++ 代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 30;
ll a[N],m[N];
int n;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0){
x = 1, y = 0;
return a;
}
ll d,x1,y1;
d = exgcd(b, a % b, x1, y1);
x = y1, y = x1 - a / b * y1;
return d;
}
ll excrt(ll a[], ll m[])
{
ll m1, m2, a1, a2, p, q;
m1 = m[1], a1 = a[1];
for (int i = 2; i <= n; i++){
m2 = m[i], a2 = a[i];
ll d = exgcd(m1, m2, p, q);// d=(m1,m2),同时计算m1p+m2q=1的特解(q=-y)
if ((a2 - a1) % d) return -1;
p = p * (a2 - a1) / d;// 这是m1p+m2q=a2-a1特解
// 保证m2/d为正数
p = (p % abs(m2 / d) + abs(m2 / d)) % abs(m2 / d);// 由于p可能是整数,需要加余取余操作变为最小正整数
a1 = m1 * p + a1;
m1 = m1 * m2 / d;
}
return (a1 % m1 + m1) % m1;// 同上
}
int main()
{
cin >> n;
// 这里与题目相反,一般是m是余数
for (int i = 1; i <= n; i++) cin >> m[i] >> a[i];
cout << excrt(a, m) << endl;
return 0;
}

P4777 【模板】扩展中国剩余定理(EXCRT)

题目描述

给定 n 组非负整数 ai,bi ,求解关于 x 的方程组的最小非负整数解。

{xb1(moda1)xb2(moda2)xbn(modan)

输入格式

输入第一行包含整数 n

接下来 n 行,每行两个非负整数 ai,bi

输出格式

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

输入输出样例 #1

输入 #1

3
11 6
25 9
33 17

输出 #1

809

说明/提示

对于 100% 的数据,1n1051bi,ai1012,保证所有 ai 的最小公倍数不超过 1018

请注意程序运行过程中进行乘法运算时结果可能有溢出的风险。

数据保证有解。

算法

同样为扩展中国剩余定理的板子题。

但是注意不要越界,函数 mulget 就在解决这个问题。

mul 函数可以快速计算 a×b % m,所以利用它可以计算 p=p0×|(a2a1)(m1,m2)| % m2(m1,m2)

代码

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false)
typedef long long ll;
const int N = 1e5 + 10;
ll a[N], b[N];
int n;
ll mul(ll a, ll b, ll m)// 计算a * b % m
{
auto res = 0ll;
while (b){
if (b & 1) res = (res + a) % m;
a = (a + a) % m;
b >>= 1;
}
return res;
}
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if (b == 0){
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
inline ll get(ll a, ll b)// 保证最小整数
{
return (a % b + b) % b;
}
ll excrt(ll a[], ll m[])
{
ll m1, m2, a1, a2, p, q;
m1 = m[1], a1 = a[1];
auto ans = 0ll;
for (int i = 2; i <= n; i++){
m2 = m[i], a2 = a[i];
ll d = exgcd(m1, m2, p, q);
// 合并成ap + bq = c
ll a = m1, b = m2, c = get(a2 - a1, m2);
if (c % d) return -1;
p = mul(p, c / d, b / d);
ans = a1 + m1 * p;
m1 = m2 / d * m1;
ans = get(ans, m1);
a1 = ans;
}
return ans;
}
int main()
{
IOS;
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i] >> b[i];
cout << excrt(b, a) << endl;
return 0;
}
posted @   AKgrid  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示