扩展中国剩余定理(简介)

扩展中国剩余定理(普通线性同余不等式组)板子题

对于中国剩余定理来说,每一个线性同余式所要求的的模数均为素数,这就在一定程度上限制了中国剩余定理的使用(但不能否认这是一个很有用而且很好用的算法)

因此,我们需要找到一种对于模数不互质的线性同余式组的更普遍的求解算法

扩展中国剩余定理构造求解

  • 对于一个线性同余方程组:

  • 我们首先假定一个数 \(x\) 为线性同余式组的前 \(k-1\) 个解,此时有 \(M = lcm ( m1,m2,……,m_i )\)

    则对于前 \(k-1\) 个线性同余式的通解为:\(x + i \times M ( i \in Z)\)

    那么对于第 \(k\) 个线性同余式,我们只需要找到一个数 \(t\) ,满足 \(x + t \times M \equiv a_k (\bmod m_k )\) 即可

    整理一下,即可得到:\(t \times M \equiv a_k - x ( \bmod m_k )\),对于整理得到的式子,我们就可以用扩展欧几里得算法求得 \(t\) 的值,再对答案进行累加即可

    所以,依次类推,对含有 \(k\) 个线性同余式的同余式组,只需使用 \(k-1\) 次扩展欧几里得算法即可求得答案

下面放代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<math.h>
#include<algorithm>
#define ll long long
using namespace std;

const ll maxn=1e6+10;
ll ai[maxn],bi[maxn];
ll n;
ll M,ans,bx;

inline 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,x,y);
	ll z=x;
	x=y;
	y=z-(a/b)*y;
	return d;
}

inline ll multi(ll a,ll b,ll p)	 //龟速乘,避免乘法溢出 
{
	ll ret=0;
	
	while(b)
	{
		if(b&1)
		{
			ret=(ret+a)%p;
		}
		a=(a+a)%p;
		b>>=1;
	}
	return ret;
}

inline ll crt()     			// excrt 求解 
{
	ll x,y,k,t;
	
	ll M=bi[1],ans=ai[1];		//初始时对 ans 和  M 进行预处理,将第一个同余式中的模数与余数赋给 M 和 ans ,作为第一组解 
	
	for(int i=2;i<=n;i++) 		//注意要进行 n-1 次循环 
	{
		ll a=M;
		ll b=bi[i];
		ll c=(ai[i]-ans%b+b)%b;  
		ll g=exgcd(a,b,x,y);	//在进行扩展欧几里得算法时,求解的是 M * x + b[i] * y = gcd( M , b[i] ) 中 x 和 y 的值 , 并可顺便将 gcd( M , b[i] ) 求得 
		if(c%g!=0) return -1; 	//目标解不能被 gcd ( M , b[i] ) 整除 , 说明该同余式无解 
		bx=b/g;
		x=multi(x,c/g,bx);
		ans+=x*M;		//累加答案 
		M*=bx;			//求 b[1] 到 b[i] 的 lcm
		ans=(ans%M+M)%M;
	}
	
	return (ans%M+M)%M;
}

int main(void)
{
	scanf("%lld",&n);
	
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&bi[i],&ai[i]);
	}
	
	printf("%lld\n",crt());
	
	return 0;
}
posted @ 2020-07-22 14:30  雾隐  阅读(179)  评论(0编辑  收藏  举报