扩展中国剩余定理

在介绍扩展中国剩余定理之前,让我们先介绍一下中国剩余定理

中国剩余定理

中国剩余定理是用来求解一堆线性同余方程组的通解的算法。
该算法的时间复杂度是 O ( 方程的个数 ) O(方程的个数) O(方程的个数)
{ x ≡ a 1 ( m o d   m 1 ) x ≡ a 2 ( m o d   m 2 ) x ≡ a 3 ( m o d   m 3 ) . . . . . . . . . x ≡ a n ( m o d   m n ) \left\{ \begin{array}{rcl} x\equiv a_1 & {(mod\ m_1)}\\ x\equiv a_2 & {(mod\ m_2)}\\ x\equiv a_3 & {(mod\ m_3)}\\ {... ......}\\ x\equiv a_n & {(mod\ m_n)} \end{array} \right. xa1xa2xa3.........xan(mod m1)(mod m2)(mod m3)(mod mn)
其中, m 1 , m 2 , . . . , m n m_1,m_2,...,m_n m1,m2,...,mn两两互质
对于这个方程,我们很容易构造出其通解。
1.计算模数累积,令 M = ∏ i = 1 n M= \prod_{i=1}^{n} M=i=1n
2.枚举 i i i 1 1 1 n n n,将 a i ⋅ M m i ⋅ i n v ( M m i , m i ) a_i\cdot\dfrac{M}{m_i}\cdot inv \left( \dfrac{M}{m_i},m_i\right) aimiMinv(miMmi)累加到答案中,注意边累加边 m o d M modM modM
其中, i n v ( a , b ) inv\left(a,b\right) inv(a,b)表示 a m o d    b a \mod b amodb的乘法逆元,可以用 e x g c d exgcd exgcd求出。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;

const double eps = 1e-10;
const double pi = acos(-1.0);

int n;
ll a[30],m[30],M = 1;
/*
算法步骤:
计算模数积,然后遍历每个方程,让答案累加a[i]*M/m[i]*inv(M/m[i],m[i]);
其中inv(a,b)表示a在modb意义下的逆元。
注意,中国剩余定理必须要满足模数两两互质。
*/

template<class T> T gcd(T a, T b){
	return !b?a:gcd(b,a%b);
}

void exgcd(ll a, ll b, ll& x, ll& y){
	if(!b){
		x = 1, y = 0;
		return;
	}
	exgcd(b,a%b,y,x);
	y -= a/b*x;
}

ll inv(ll a, ll b){
	ll x,y;
	exgcd(a,b,x,y);
	return (x%b+b)%b;
}

void solve(){
    scanf("%d",&n);
	for(int i = 1; i <= n; i++){
		scanf("%lld%lld",&m[i],&a[i]);
		M *= m[i];
	}
	ll ans = 0;
	for(int i = 1; i <= n; i++){
		ans = (ans + a[i]*M/m[i]*inv(M/m[i],m[i]))%M;
	}
	printf("%lld\n",(ans%M + M)%M);
}

int main()
{
	solve();
	return 0;
}

扩展中国剩余定理

{ x ≡ a 1 ( m o d   m 1 ) x ≡ a 2 ( m o d   m 2 ) x ≡ a 3 ( m o d   m 3 ) . . . . . . . . . x ≡ a n ( m o d   m n ) \left\{ \begin{array}{rcl} x\equiv a_1 & {(mod\ m_1)}\\ x\equiv a_2 & {(mod\ m_2)}\\ x\equiv a_3 &{(mod\ m_3)}\\ {... ......}\\ x\equiv a_n & {(mod\ m_n)} \end{array} \right. xa1xa2xa3.........xan(mod m1)(mod m2)(mod m3)(mod mn)
这时的方程模数不再满足两两互质,甚至还有可能无解。
还是老样子,我们先考虑简单情况。
先看前两个方程组:
{ x ≡ a 1 ( m o d   m 1 ) x ≡ a 2 ( m o d   m 2 ) \left\{ \begin{array}{rcl} x\equiv a_1 &{( mod\ m_1)}\\ x\equiv a_2 &{(mod\ m_2)}\\ \end{array}\right. {xa1xa2(mod m1)(mod m2)
它等价于
{ x = a 1 + k 1 m 1 x = a 2 + k 2 m 2 \left\{ \begin{array}{rcl} x= a_1+k_1m_1\\ x= a_2+k_2m_2\\ \end{array}\right. {x=a1+k1m1x=a2+k2m2
于是消去 x x x,我们就得到了一个方程:
k 1 m 1 − k 2 m 2 = a 2 − a 1 k_1m_1-k_2m_2 = a_2-a_1 k1m1k2m2=a2a1
显然,这是个简单的二元不定方程。
如果 g c d ( m 1 , m 2 ) ∤ a 2 − a 1 gcd(m1,m2)\nmid a_2-a_1 gcd(m1,m2)a2a1,那么方程组无解。
否则我们可以用 e x g c d exgcd exgcd求出一组解。
设这组解为 k 1 ∗ , k 2 ∗ k_1^{*},k_2^{*} k1,k2,那么我们可以得到这组方程组的通解:
{ k 1 = k 1 ∗ + m 2 g c d ( m 1 , m 2 ) t k 2 = k 2 ∗ + m 1 g c d ( m 1 , m 2 ) t \left\{ \begin{array}{rcl} k_1= k_1^{*}+\dfrac{m_2}{gcd(m_1,m_2)}t\\ \\ k_2= k_2^{*}+\dfrac{m_1}{gcd(m_1,m_2)}t\\ \end{array}\right. k1=k1+gcd(m1,m2)m2tk2=k2+gcd(m1,m2)m1t
将第一个方程带入,得
x = a 1 + k 1 ∗ m 1 + m 1 m 2 d t = a 1 + k 1 ∗ m 1 + t [ m 1 , m 2 ] \begin{aligned} x=&{a_1+k_1^{*}m_1+\frac{m_1m_2}{d}t}\\ =&{a_1+k_1^{*}m_1+t[m_1,m_2]} \end{aligned} x==a1+k1m1+dm1m2ta1+k1m1+t[m1,m2]
x 0 = a 1 + k 1 ∗ m 1 x_0=a_1+k_1^{*}m_1 x0=a1+k1m1
那么原来的两个方程可以转化为一个方程
x = x 0 + t [ m 1 , m 2 ] x=x_{0}+t[m_1,m_2] x=x0+t[m1,m2]
这样,我们可以通过n-1次循环将n个方程合并为1个方程,最后通过解最后一个方程得到整个方程组的解。
算法步骤:
1.初始化 a 1 a_1 a1 m 1 m_1 m1;
2.对于第 i i i个方程,令 a 2 = a i a_2 = a_i a2=ai m 2 = m i m_2 = m_i m2=mi,对其用拓展欧几里得算法: d = e x g c d ( m 1 , m 2 , k 1 , k 2 ) d = exgcd(m_1,m_2,k_1,k_2) d=exgcd(m1,m2,k1,k2)
如果 d ∤ ( a 2 − a 1 ) d\nmid (a_2-a_1) d(a2a1)那么无解;
否则 k 1 ∗ = a 2 − a 1 d k_1 *= \dfrac{a_2-a_1}{d} k1=da2a1
这里我们只需要 k 1 k_1 k1的值,故不用对 k 2 k_2 k2进行任何操作。
4.为了不让结果溢出且恒为正,我们需要做一些预处理:令 t = m 2 d t = \dfrac{m_2}{d} t=dm2,然后 k 1 = ( k 1 % t + t ) % t k_1 = (k_1\%t+t)\%t k1=(k1%t+t)%t
a 1 = a 1 + k 1 m 1 , m 1 = ∣ l c m ( m 1 , m 2 ) ∣ a_1=a_1+k_1m_1,m_1=\left|lcm(m_1,m_2)\right| a1=a1+k1m1,m1=lcm(m1,m2)
重复循环 n − 1 n-1 n1次,我们可以得到一个方程
x = a 1 + t m 1 x = a_1 + tm_1 x=a1+tm1
所得结果就是 ( a 1 % m 1 + m 1 ) % m 1 \left(a_1\%m_1+m_1\right)\%m_1 (a1%m1+m1)%m1
各个变量关系非常复杂,建议在考场上实现代码的是由用手推一下式子。

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;

const double eps = 1e-10;
const double pi = acos(-1.0);
const int maxn = 1e6 + 10;

int n;

inline ll mul(ll a,ll b,ll mod){//O(1)取模快速乘,不会爆long long
	return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;
}

ll exgcd(ll a, ll b, ll& x, ll& y){
	if(!b){
		x = 1, y = 0;
		return a;
	}
	ll d = exgcd(b,a%b,y,x);
	y -= a/b*x;
	return d;
}

void solve(){
	ll a1,m1;
	scanf("%d",&n);
	scanf("%lld%lld",&m1,&a1);
	bool ok = 1;
	for(int i = 2; i <= n; i++){
		ll a2,m2,k1,k2;
		scanf("%lld%lld",&m2,&a2);
		ll d = exgcd(m1,m2,k1,k2);
		if((a2-a1)%d) ok = 0;
		else{
			k1 = mul(k1,(a2-a1)/d,m2/d);//这个地方必须要用取模快速乘
			a1 = a1+k1*m1;
			m1 = abs(m1/d*m2);
		}
	}
	if(ok)printf("%lld\n",(a1%m1+m1)%m1);
}

int main()
{
	solve();
	return 0;
}
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值