chinesert

https://blog.csdn.net/wu_tongtong/article/details/79669723

(很久)之前就已经接触过CRT

我们先看简单的CRT

xa1  (mod m1)


xa2  (mod m2)
xa3  (mod m3)
...
xak  (mod mk)
其中m1,m2,...,mk两两互质,求在mod mi意义下的x

这种CRT可以说是最基础的了
M=ki=1mi


对于每一个方程,Mi=Mmi
ti=M1i(逆元)
tiMi=1  (mod mi)
x的通式形式为:
x=a1t1M1+a2t2M2+...aktkMk+kM=kM+ki=1aitiMi


在模M的意义下,方程组只有一个解:

x=i=1kaitiMi
ll x,y;

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

ll CRT() {
    ll M=1,ans=0;
    for (int i=1;i<=n;i++) M*=m[i];
    for (int i=1;i<=n;i++) {
        ll Mi=M/m[i];
        exgcd(Mi,m[i]);
        ans+=x*Mi*a[i]; ans%=M;
    }
    return ans; 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

注意这里求逆元的方式是exgcd
不过要使用ta的前提条件是gcd(a,b)=1

(exgcd求解的方程等式右边一定是gcd(a,b)k

进阶版

如果不满足m两两互质,我们采用合并同余方程的方法将方程两两合并

x=a1  (mod m1)                               (0)


x=a2  (mod m2)

两个方程可以写成
x=m1t1+a1                                 (1)


x=m2t2+a2

m1t1+a1=m2t2+a2


m1t1=(a2a1)+m2t2
m1t1=(a2a1)  (mod m2)

显然,想要有解,必有gcd(m1,m2)|(a2a1)


gcd(m1,m2)=d,c=a2a1

,则有:

m1dt1=cd         (mod m2d)


t1=cd(m1d)1  (mod m2d)
 K=cd(m1d)1,t1=ym2d+K,(1)

x=m1(ym2d+K)+a1


=m1m2dy+m1K+a1

即:
x=m1K+a1  (mod  m1m2d)


x=a                 (mod n)                      (2)

(2)


a=m1K+a1,n=m1m2d

这样,成功的将(0)

式的两个方程合并为式(2)
最终,合并k个方程的最小x值为a%n

ll x,y,d;

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

ll CRT() {
    while (scnaf("%d",&n)!=EOF) {
        ll a1,a2,m1,m2;
        bool ff=1;
        for (int i=1;i<=n;i++) {
            if (i==1) {
                scanf("%lld%lld",&a1,&m1);    //x=a (mod m)
                continue;
            }
            scanf("%lld%lld",&a2,&m2);
            exgcd(m1,m2);
            if ((a2-a1)%d) ff=0;              //gcd(m1,m2)|(a2-a1)
            x=x*((a2-a1)/d);
            ll p=m2/d;                        //1x=(x%p+p)%p;
            a1=a1+m1*x;                       //新余数 
            m1=m1/d*m2;                       //新模数  m1m2/d 
            a1=(a1%m1+m1)%m1;                 //2式 两次取模保证是最小整数解 
        }
        if (ff) printf("%lld\n",a1);
        else printf("-1\n");
    }
}
posted @ 2020-03-19 15:29  zJanly  阅读(161)  评论(0编辑  收藏  举报