模線性方程組:
給定了n組除數m[i]和余數r[i]傀蚌,通過這n組(m[i],r[i])求解一個z氏豌,使得z % m[i] = r[i]
首先,從最簡單的情況入手,只有兩條方程:
設z%m0=r0, z%m1=r1;
=>z=r0+m0*k0, z=r1+m1*k1( k1,k2 為常數 )
=>r0+m0*k0=r1+m1*k1
=>m0*k0-m1*k1=r1-r0
設m0為A,m1為B,k0為x,-k1為y,r1-r0為C,則A*x+B*y=C;
所以很容易想到,這個可以用擴展歐幾里德來求
求出x后,求出z0=r0+m0*k0=r0+m0*x
同時,我們將這個z0作為特解,可以擴展出一個解系:
Z = z0 + k*lcm(m0, m1) k為整數
化為方程式可以得到Z%lcm(m0, m1)=z0;
設M=lcm(m0, m1),R=z0,所以Z%M=R;
此時空入,可以發(fā)現我們將z mod m0= r0,z mod m1 = r1合并為了一個式子Z mod lcm(m0, m1) = z0。滿足后者的X一定滿足前兩個式子冷尉。
每兩個式子都可以通過該方法化簡為一個式子。那么我們只要重復進行這個操作系枪,就可以將n個方程組化簡為一個方程雀哨,并且求出一個最后的解了。
模板:
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
const int MAXN=15;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
LL res=exgcd(b,a%b,x,y);
LL tmp=x;
x=y;
y=tmp-a/b*y;
return res;
}
LL solve(LL m[MAXN],LL r[MAXN],int n)
{
LL x,y;
LL M=m[0],R=r[0],C,gcd;
for(int i=1;i<n;i++)
{
gcd=exgcd(M,m[i],x,y);
C=r[i]-R;
if(C%gcd!=0) return -1;
x=(x/gcd*C)%(m[i]/gcd);//x*C/gcd為方程的初解x0,x0%(m[i]/gcd)為最小的正整數解,避免溢出
R=M*x+R;//z = m[i] * k[i] + r[i]
M=M*m[i]/gcd;//求出最小公倍數 lcm(M,m[i])
R%=M;// 求解合并后的新R私爷,同時讓R最小
}
if(R<0)
{
R+=M;
}
return R;
}
LL m[MAXN],r[MAXN];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=0;i<n;i++)
{
scanf("%lld%lld",m+i,r+i);
}
printf("%lld\n",solve(m,r,n));
}
}