拓展中国剩余定理(excrt)

问题

求解同余方程组

其中各个方程的模数为不一定两两互质的整数, 求x的最小非负整数解

求解

假设已经求出前k-1个方程组成的同余方程组的一个解为x

且有M=lcm(mo[1],mo[2],mo[3],...,mo[k-1])

则前k-1个方程的方程组通解为x+i*M 

因为M为前面方程模数的最小公倍数,所以M可以整除任意一个模数,所以方程组加上或减去M对于方程的同余性没有影响。

那么对于加入第k个方程后的方程组

我们就是要求一个正整数t,使得 x+tM≡yu[k](mod mo[k])

转化一下上述式子得 tM≡yu[k]x(mod mo[k])

对于这个式子我们已经可以通过扩展欧几里得求解t

若该同余式无解,则整个方程组无解, 若有,则前k个同余式组成的方程组的一个解解为xk=x+t*M

所以整个算法的思路就是求解k次扩展欧几里得

洛谷 P4777

#include<bits/stdc++.h>
using namespace std;
#define ll __int128
ll ans,x,y,a,b,c,yu[100005],mo[100005],M,n;
long long read()//int 128好像只能用快读
{
    ll f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
long long gcd(ll a,ll b)
{
	ll c;
	while(a%b)
	{
		c=a%b;
		a=b;
		b=c;
	}
	return b;
}
long long lcm(ll a,ll b)
{
	return a/gcd(a,b)*b;
}
void exgcd(ll a,ll b)
{
	if(b==0)
	{
		x=1;y=0;
		return;
	}
	exgcd(b,a%b);
	ll tem=x;
	x=y;
	y=tem-a/b*y;
}
long long excrt()
{
	M=mo[1];ans=yu[1];//特判第一个方程
	for(int i=2;i<=n;i++)
	{
		exgcd(M,mo[i]);//求解方程 t*M+y*mo[i]=yu[i]-ans;
		ll gcdd=gcd(M,mo[i]);
		if((yu[i]-ans)%gcdd!=0)return -1;//如果(yu[i]-ans)不是gcd(M,mo[i])的倍数,则根据裴蜀定理,方程无解
		x*=(yu[i]-ans)/gcdd;//刚才求出的是方程 t*M+y*mo[i]==gcd(M,mo[i])的解,现在将方程解出的x,y同时扩大倍数,就成了方程真正的解。但y没有用,只是个比例系数,就不管了。
		ans=ans+x*M;//上面的解析有说,更新答案
		M=M/gcd(M,mo[i])*mo[i];//更新M的值
		ans=(ans%M+M)%M;//之前的答案只是一个肯定成立的解,现在把ans改成在当前余数系下的最小非负整数答案
	}
	return ans;
}
int main()
{
    n=read();
    for(ll i=1;i<=n;i++)
        mo[i]=read(),yu[i]=read();
    cout<<excrt();
    return 0;
}

  

原文地址:https://www.cnblogs.com/oierjzy/p/11232187.html