问题
求解同余方程组
其中各个方程的模数为不一定两两互质的整数, 求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+t∗M≡yu[k](mod mo[k])
转化一下上述式子得 t∗M≡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; }