好像OI界都喜欢什么扩展XX,扩展就很不好理解了所以我们干脆给他用一个比较好理解的名字叫合并模线性方程。
先简单介绍下线性同余方程 ax%n=b 其有解需满足(a,n)|b,令d=gcd(a,n) 设t为(a/d)x % (n/d) = (b/d)的唯一解,则ax%n=b的d个解为t,t+n/d,t+2n/d…t+(d-1)n/d。
问题模型:给定模线性同余方程组 r[i]%a[i]=m (1<i<=n,r[i],a[i]均为正整数),判断是否存在最小非负整数解,为一般方程不考虑中国剩余定理的情形。先看前两个方程a1x+r1=m a2y+r2=m, 可以写成ax-by=r2-r1 方程有解需保证(a,b)|(r2-r1),问题等价于寻求ax+by=n的解,我们利用扩展欧几里得先求得ax+by=(a,b)的一组解(x0,y0),于是(x1,y1)=(x0,y0)n/(a,b)为ax+by=n的解。 其他解 x_1,2…≡x1+(b/(a,b))t,y_1,2…≡y1-(a/(a,b))t (0 <=t<=(a,b)-1)。设d=gcd(a,b) x=x+(a2/d)t,则x的最小非负整数解为 x=(x%(a2/d)+(a2/d))%(a2/d); 令f=a2/d;则x=(x%f+f)%f;新的方程变为a1(x+(a2/d)t)+r1=m我们化简得(a1a2/d)t+(a1x+r1)=m r1=a1x+r1; a1=lcm(a1,a2)=(a1a2)/d,故m=r1(mod a1)两个线性同余方程合并成一个等效方程,并继续合并下去,最后r1就是方程组的解了,该方法复杂度为O(nextend_gcd)。
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll ai[1000005],bi[1000005],n;
inline ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b)
{
x=1;
y=0;
return a;
}
ll gcd = exgcd(b, a % b, x, y);
ll t=x;
x=y;
y = t-(a / b) * y;//递归解方程。
return gcd;
}
inline ll mul(ll a, ll b, ll mod) //必须自己打乘法不然会爆long long
{
ll s = 0;
while (b)
{
if (b & 1) s = (s + a) % mod;
a = (a + a) % mod;
b >>= 1;
}
return s % mod;
}
ll excrt()
{
ll x,y,k;
ll M=bi[1],ans=ai[1];//第一个方程的解特判
for(int i=2;i<=n;i++)
{
ll a=M,b=bi[i],c=(ai[i]-ans%b+b)%b;//ax≡c(mod b)
ll gcd=exgcd(a,b,x,y),bg=b/gcd;
if(c%gcd!=0) return -1; //判断是否无解,然而这题其实不用
x=mul(x,c/gcd,bg);
ans+=x*M;//更新前k个方程组的答案
M*=bg;//M为前k个m的lcm
ans=(ans%M+M)%M;
}
return (ans%M+M)%M;
}
int main()
{
ll now,lcm,k;
ll x,y;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>bi[i]>>ai[i];
}
cout<<excrt();
return 0;
}