例题P4777 【模板】扩展中国剩余定理(EXCRT)
对于每一个x≡b1(moda1),可以设k为x/a1的商,易得an∗kn+bn=x。
对于前两个式子
{a1∗k1+b1=xa2∗k2+b2=x
可得a1∗k1+b1=a2∗k2+b2
即a1∗k1−a2∗k2=b2−b1
通过扩展欧几里得算法,不难算出k1,k2的所有解为
{k1+k∗da2k2+k∗da1
(代入原式即可看出)
对于每个k1,k2取最小(避免爆long long)可得
k1=(k1modt+t)modt
t=a2/d
由于扩展欧几里得求出的k1,k2是满足a1∗k1−a2∗k2=d(d为a1,a2的最大公约数)
要满足a1∗k1−a2∗k2=m2−m1,就让k1再乘上db2−b1,
此时,再将k1
代入原式得
a1∗(k1+k∗da1∗a2)+b1=x=a1∗k1+b1+k∗da1∗a2
设x0=a1∗k1+b1,a=da1∗a2
可得k∗a+x0=x
仔细观察发现与上面的式子出乎意料的一样,所以我们就可以反复重复上述操作,将所有式子最终合并为一个式子,最后x=(x0%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; }
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"); }
|