EXCRT
是个好东西,可以处理在模数不互质的同余方程组
核心就是用扩欧来合并方程
如果我们有两个形如\(x\equiv b_1(mod\ a_1)\) \(x\equiv 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_1\equiv 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
\]
现在我们再写成同余式
\[x\equiv 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;
}