是个好东西,可以处理在模数不互质的同余方程组
核心就是用扩欧来合并方程
如果我们有两个形如(xequiv b_1(mod a_1)) (xequiv b_2(mod a_2))的方程我们要将他们合并
就是利用各种操作化柿子
[x=a_1k_1+b_1=a_2k_2+b_2
]
随便移一下
[a_1k_1=b_2-b_1+a_2k_2
]
根据贝祖定理((a1,a2)|(b_2-b_1))时候才有解
如果有解得话,我们只需要在两边除以((a1,a2))
那么就有
[frac{a_1k_1}{(a_1,a_2)}=frac{b_2-b_1}{(a_1,a_2)}+frac{a_2k_2}{(a_1,a_2)}
]
我们也可以写成同余的形式
[frac{a_1k_1}{(a_1,a_2)}equiv frac{b_2-b_1}{(a_1,a_2)}(mod frac{a_2}{(a_1,a_2)})
]
让左边只留下(k_1),就是把逆元乘过去
[k_1equiv inv(frac{a_1}{(a_1,a_2)},frac{a_2}{(a_1,a_2)})*frac{b_2-b_1}{(a_1,a_2)}(mod frac{a_2}{(a_1,a_2)})
]
再将同余式写成等式
[k_1=inv(frac{a_1}{(a_1,a_2)},frac{a_2}{(a_1,a_2)})*frac{b_2-b_1}{(a_1,a_2)}+frac{a_2}{(a_1,a_2)}*y
]
再回带到(x)里去
[x=inv(frac{a_1}{(a_1,a_2)},frac{a_2}{(a_1,a_2)})*frac{b_2-b_1}{(a_1,a_2)}*a_1+frac{a_2a_1}{(a_1,a_2)}*y+b_1
]
现在我们再写成同余式
[xequiv inv(frac{a_1}{(a_1,a_2)},frac{a_2}{(a_1,a_2)})*frac{b_2-b_1}{(a_1,a_2)}*a_1\%frac{a_2}{(a_1,a_2)}+b_1(mod frac{a_1a_2}{(a_1,a_2)})
]
现在两个方程不就被合并好了吗
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#define re register
#define maxn 100005
#define LL long long
LL x,y;
LL gcd(LL a,LL b)
{
if(!b) return a;
return gcd(b,a%b);
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b) return x=1,y=0,a;
LL r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
int n;
LL a[maxn],b[maxn];
inline LL read()
{
char c=getchar();
LL x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
inline LL inv(LL a,LL b)
{
LL r=exgcd(a,b,x,y);
LL t=b/r;
return (x%t+t)%t;
}
inline LL mul(LL a,LL b)
{
LL S=0;
while(b)
{
if(b&1ll) S=S+a;
b>>=1ll;
a=a+a;
}
return S;
}
void write(LL x)
{
if(x>9) write(x/10);
putchar(x%10+48);
}
int main()
{
n=read();
for(re int i=1;i<=n;i++)
a[i]=read(),b[i]=read();
LL r=gcd(a[1],a[2]);
LL A=mul(a[1],a[2]/r);
LL B=(inv(a[1]/r,a[2]/r)*(b[2]-b[1])/r%(a[2]/r)+a[2]/r)%(a[2]/r)*a[1]+b[1];
for(re int i=3;i<=n;i++)
{
LL r=gcd(A,a[i]);
B=(inv(A/r,a[i]/r)*(b[i]-B)/r%(a[i]/r)+a[i]/r)%(a[i]/r)*A+B;
A=mul(A,a[i]/r);
}
write(B);
return 0;
}