扩展中国剩余定理(EXCRT)快速入门

问题

传送门

看到这个问题感觉很难???
不用怕,往下看就好啦
假如你不会CRT也没关系

EXCRT大致思路

先考虑将方程组两两联立解开,如先解第一个与第二个,再用第一个与第二个的通解来解第三个...(以此类推)
那么怎么解第一个与第二个同余方程呢?

\[\begin{cases} x \equiv a_1 \pmod{b_1}\\ x \equiv a_2 \pmod{b_2}\\ . . . \end{cases} \]

则存在整数(注意不是非负),使得

\[\begin{cases} x = a_1 +k_1 * b_1\\ x = a_2 +k_2 * b_2\\ \end{cases} \]

所以有
$ a_1 +k_1*b_1$ \(=\) $a_2 +k_2 * b_2 $
移项可得
$ k_1*b_1 $ \(+\) $k_2*b_2=a_2-a_1 $

(注意这里可以是 $ k_1*b_1$ \(+\) $k_2*b_2 $ 因为 $ k_2 $ 可为负数)

不妨令\(a_2-a_1\)\(c\)
然后发现这里长得很像我们的 扩展欧几里得 !!!
而根据裴蜀定理得,当且仅当 $gcd( k_1 , k_2 ) | c $ 时 该方程有整数解
所以我们可用exgcd求出 $ k_1*b_1+k_2*b_2=gcd(k_1,k_2) $
再等式两边同时乘以 $ c/gcd(k_1,k_2) $ 即可

这样就可以解出 此时的式子了
所以新的 A 为\(a_1+b_1*k_1\) ,新的B则为\(lcm(b_1,b_2)\)
然后再用新的A,B与\(a_3,b_3\)去计算就好
最后的答案就是最后算出来的A

代码

#include<bits/stdc++.h>
using namespace std;
#define re register
#define int __int128
#define in inline 
#define get getchar()
in int read()
{
    int t=0; char ch=get;
    while(ch<'0' || ch>'9') ch=get;
    while( ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    return t;
}
const int _=2e5+5;
int a[_],b[_],n;
void out(int x) {
    if (!x) return;
    out(x / 10);
    putchar(x % 10 + '0');
}
in int exgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1,y=0;
        return a;
    }
    int g=exgcd(b,a%b,y,x);
    y -= a / b * x;
    return g;
}
in int mul(int a,int b,int mod)
{
    int res=0;
    while(b)
    {
        if(b&1)res=(res+a)%mod;
        a=(a+a)%mod,b>>=1;
     }
    return res;
}
in int excrt()
{
    int a1=a[1],b1=b[1];
    for(re int i=2;i<=n;i++)
    {
        int a2=a[i],b2=b[i],x,y,c;
        c=((a2-a1)%b2+b2)%b2;
        int gcd=exgcd(b1,b2,x,y);
        if(c%gcd) return -1;
        x=mul(x,c/gcd,b2);
        a1=a1+x*b1;
        b1=b1*b2/gcd;
        a1=(a1%b1+b1)%b1;
    }
    return a1;
}
signed main()
{
    n=read();
    for(re int i=1;i<=n;i++)
        b[i]=read(),a[i]=read();
    out(excrt());
    return 0;
}

posted @ 2019-09-24 16:28  yzhx  阅读(227)  评论(1编辑  收藏  举报