例题P4777 【模板】扩展中国剩余定理(EXCRT)

对于每一个xb1(moda1)x≡ b_1(\mod a_1),可以设k为x/a1x/a_1的商,易得ankn+bn=xa_n*k_n+b_n=x
对于前两个式子

{a1k1+b1=xa2k2+b2=x\begin{cases} a_1*k_1+b_1=x \\ a_2*k_2+b_2=x \end{cases}

可得a1k1+b1=a2k2+b2a_1*k_1+b_1=a_2*k_2+b_2
a1k1a2k2=b2b1a_1*k_1-a_2*k_2=b_2-b_1
通过扩展欧几里得算法,不难算出k1,k2k_1,k_2的所有解为

{k1+ka2dk2+ka1d\begin{cases} k_1+k*\frac{a2}{d} \\ k_2+k*\frac{a1}{d} \end{cases}

(代入原式即可看出)
对于每个k1,k2k_1,k_2取最小(避免爆long long)可得
k1=(k1modt+t)modtk_1=(k_1\mod t+t)\mod t

t=a2/dt=a_2/d

由于扩展欧几里得求出的k1,k2k_1,k_2是满足a1k1a2k2=da_1*k_1-a_2*k_2=d(d为a1,a2a_1,a_2的最大公约数)

要满足a1k1a2k2=m2m1a_1*k_1-a_2*k_2=m_2-m_1,就让k1k_1再乘上b2b1d\frac{b_2-b_1}{d},

此时,再将k1
代入原式得

a1(k1+ka1a2d)+b1=x=a1k1+b1+ka1a2da_1*(k1+k*\frac{a_1*a_2} {d})+b_1=x=a_1*k_1+b_1+k*\frac{a_1*a_2}{d}

x0=a1k1+b1,a=a1a2dx_0=a_1*k_1+b_1,a=\frac{a_1*a_2}{d}
可得ka+x0=xk*a+x_0=x

仔细观察发现与上面的式子出乎意料的一样,所以我们就可以反复重复上述操作,将所有式子最终合并为一个式子,最后x=(x0%a+a)%ax=(x_0\%a+a)\%a

需要注意的是这到题可能会再乘法的时候爆long long,所以需要用到快速乘
龟速乘(或者__int128)
代码如下

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;

inline ll mul(ll a, ll b, ll m)
{
ll c = a*b-(ll)((long double)a*b/m+0.5)*m;
return c<0 ? c+m : c; //a*b%m
}

ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(!b)
{
x = 1, y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}

int main()
{
bool is = true;
ll n;
scanf("%lld", &n);
ll a1, m1;
scanf("%lld%lld", &a1, &m1);
for (int i = 1; i < n;i++)
{
ll ai, mi, k1, ki;
scanf("%lld%lld", &ai, &mi);
ll d = exgcd(a1, ai, k1, ki);
if((mi-m1)%d)
{
is = false;
break;
}
ll t = ai / d;
k1 = mul(k1, (mi - m1) / d, t );
k1 = (k1 % t + t) % t;
m1 = a1 * k1 + m1;
a1 = abs(ai / d * a1);
}
if(is)
printf("%lld", (m1 % a1 + a1) % a1);
else
printf("-1");
}