中国剩余定理及拓展

中国剩余定理
x % m1m_1m1 = a1a_1a1 (1)
x % m2m_2m2 = a2a_2a2 (2)

x % mnm_nmn = ana_nan (n)
m1m_1m1m2m_2m2,…mnm_nmn两两互质,求解x

定理推导
设Mi=∏j=1,j!=inmj设M_i = \prod_{j=1,j!=i}^nm_jMi=j=1,j!=inmj
∵对于任意的j!=i,Mi%mj=0,Mi%mi!=0\because对于任意的j!=i,M_i \% m_j = 0,M_i\%m_i!=0j!=i,Mi%mj=0,Mi%mi!=0
∴设Ki=Mi∗Mi−1+ai(Mi−1为mi下的逆元)\therefore设K_i=M_i*M_i^{-1}+a_i(M_i^{-1}为m_i下的逆元)Ki=MiMi1+aiMi1mi
那么对于任意的j!=i,Ki%mj=0,Ki%mi==ai那么对于任意的j!=i,K_i \% m_j = 0,K_i\%m_i==a_ij!=i,Ki%mj=0,Ki%mi==ai
∴x=∑i=0nKi\therefore x = \sum_{i=0}^n K_ix=i=0nKi



扩展中国剩余定理
x % m1m_1m1 = a1a_1a1 (1)
x % m2m_2m2 = a2a_2a2 (2)

x % mnm_nmn = ana_nan (n)
m1m_1m1m2m_2m2,…mnm_nmn两两不一定互质,求解x
推导过程
若n = 1,显然x = km1m_1m1 + a1a_1a1 (k为任意数)
所以我们尝试合并方程即可
将(1)式(2)式拿出
k1m1+a1k_1m_1 + a_1k1m1+a1 = k2m2+a2k_2m_2 + a_2k2m2+a2
k1m1−k2m2=a2−a1k_1m_1 - k_2m_2 = a_2 - a_1k1m1k2m2=a2a1
(a2−a1)(a_2-a_1)(a2a1) % gcd(m1,m2m_1,m_2m1,m2) != 0 ,则x无解
否则利用扩展欧几里得即可计算出k1k_1k1
ext_gcd(m1,m2,k1,k2m_1,m_2,k_1,k_2m1,m2,k1,k2),算出该方程等于gcd(m1,m2m_1,m_2m1,m2)时的解
然后乘以(a2−a1)/gcd(m1,m2)(a_2 - a_1) / gcd(m_1,m_2)(a2a1)/gcd(m1,m2),即可得出合并(1)式,(2)式的k1,从而算出满足(1)式(2)式的x′。即x′=k1∗m1+a1,但是问题是这个x′并不一定满足(3)式。所以我们要利用x′写出x的通解,那么x的通解就应该是x′加上lcm(m1,m2)(1)式,(2)式的k_1,从而算出满足(1)式(2)式的x'。\\即x'=k_1*m_1+a_1,但是问题是这个x'并不一定满足(3)式。\\所以我们要利用x'写出x的通解,那么x的通解就应该是x'加上lcm(m_1,m_2)(1),(2)k1,(1)(2)xx=k1m1+a1x(3)xxxxlcm(m1,m2)
所以x的通解为x′+k∗lcm(m1,m2),这样a1=x′,m1=lcm(m1,m2),这样第二个式子就被化掉了x'+k*lcm(m_1,m_2),这样a_1 = x',m_1 = lcm(m1,m2),这样第二个式子就被化掉了x+klcm(m1,m2),a1=x,m1=lcm(m1,m2),
同理合并(3)式,(4)式,…(n)式后x = k1m1+a1k_1m_1 + a_1k1m1+a1

/*
扩展中国剩余定理
求n个同余方程的解
输出最小的正整数解 
这个就算同余方程的右边是负数也能做
*/
#include <cstdio>
using namespace std;
typedef long long ll;

int n;
ll m[100005],a[100005];

ll ext_gcd(ll m1,ll m2,ll &k1,ll &k2)
{
	ll d = m1;
	if( !m2 )
	{
		k1 = 1;
		k2 = 0;
	}else
	{
		d = ext_gcd(m2,m1%m2,k2,k1);
		k2 -= (m1/m2) * k1; 
	}
	return d;
}

ll q_mul(ll a,ll b,ll mod)
{
	ll res = 0;
	ll flag = 1;
	if( b < 0 ) 
	{
		b = -b;
		flag = -1;
	}
	while( b != 0 )
	{
		if( b & 1 ) res += a;
		a += a;
		a %= mod;
		b >>= 1;
	}
	return res * flag;
}

ll ext_CRT()
{
	a[1] %= m[1];
	//if( a[1] == 0 ) a[1] += m[1]; //若要求大于0,加上这一句 
	ll k1,k2;
	for (int i = 2; i <= n; i++)
	{
		ll d = ext_gcd(m[1],m[i],k1,k2);
		if( (a[i] - a[1]) % d )   //两个式子无法合并,说明无解 
		{
			return -1;
		}
		ll t = m[i] / d;    //这里m[i]一定为正,否则直接取绝对值,因为要最小整数解
		k1 %= t;
		ll z = (a[i]-a[1])/d;
		k1 = q_mul(k1,z,t);
		k1 = (k1 % t + t) % t;
		a[1] = k1 * m[1] + a[1];
		m[1] = m[1] * ( m[i] / d );
		a[1] %= m[1];
		//if( a[1] == 0 ) a[1] += m[1];  //保证大于0 
	}
	return a[1];
}

int main()
{
	scanf("%d",&n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld%lld",&m[i],&a[i]);   // x % mi = ai 
	}
	printf("%lld\n",ext_CRT());
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值