excrt(拓展中国剩余定理)

对于一个同余方程

对于第一个和第二个式子

则有:

ans=a1+k1n1

ans=a2+k2n2

就有:

a1+k1n1=a2+k2n2

k1n1k2n2=a2a1

故我们设c=a2a1 再变化一下形式就有:

k1n1+(k2)n2=c

令 gcd=gcd(n1,n2)

这样我们就可以通过exgcd来求出一组解x1,y1

满足 x1n1+y2n2=gcd

故:x1d/gn1+y2d/gn2=gc/gcd

则: k1=x1∗c/gcd,k2=y1c/gcd

从而得到一组通解((k1+q)*n1+(k2-p)*n2=c)

k1​=k1+q=k1+n2/gcdT

k2​=k2-p=k2n1/gcdT

要使所求得的解最小且为正整数则可以根据 k1 的通解形式求得

mink1=(k1%(n2/gcd)+n2/gcd)%(n2/gcd)

再带入ans=a1+mink1n1 得到 ans

令 A 为合并后的 a , N 为合并后的 n

所以N=lcm(n1,n2)=n1n2/gcd

根据ansA (mod N) 且 ans 是满足该式子最小的值

得到: A=ans

代码

#include<bits/stdc++.h>
using namespace std;
#define ll __int128
ll ans,a[100010],n[100010],N,a1,a2,n1,n2,c,gcd,x,y;
long long read()
{
    ll f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
void exgcd(ll aa,ll bb,ll &gcd,ll &x,ll &y)
{
    if(!bb) 
    {
        gcd=aa;
        x=1,y=0;
        return;
    }
    exgcd(bb,aa%bb,gcd,x,y);
    ll tr=x;
    x=y;
    y=tr-aa/bb*y;
}
long long zs()
{
    a1=a[1],n1=n[1];
    for(ll i=2;i<=N;i++)
    {
        a2=a[i],n2=n[i];
        c=a2-a1;
        exgcd(n1,n2,gcd,x,y);//n1*x+n2*y=gcd(n1,n2)的解 
        if(c%gcd==0) 
        {
            ll k1=x*c/gcd;//n1*k1+n2*k2=c的解 
            ll mink1=(k1%(n2/gcd)+(n2/gcd))%(n2/gcd);//k1通解:k1=k1+(n2/gcd)*t
            ans=mink1*n1+a1;//这2个方程的解 
            //合并2个方程 
            n1=(n1*n2)/gcd;
            a1=ans;
        }
        else return -1;
    }
    return ans;
}
int main()
{
    N=read();
    for(ll i=1;i<=N;i++)
        n[i]=read(),a[i]=read();
    cout<<zs();
    return 0;
}

部分参考来自https://www.luogu.org/blog/sumijie/solution-p4777

猜你喜欢

转载自www.cnblogs.com/oierglh/p/11230965.html