中国剩余定理及其扩展

中国剩余定理

 

m 1 , m 2 , m 3 m n 两两互素,有同余方程组:

{ x a 1   ( m o d   m 1 ) x a 2   ( m o d   m 2 ) x a n   ( m o d   m n )

求最小的满足方程的 x

 

由于网上对中国剩余定理的基本概念讲述的差不多了,这里就直接给简单证明。

 

为了方便后面的描述,我们定义 M = i = 1 n m i   ,   w i = M m i (这个定义不用于后面的扩展中国剩余定理和其它情况)。

我们的目的是想对于每一个方程找出一个比较特殊的 p i ,使得 p i a i   m o d   m i ,且 p i 0   m o d   m j ( j i ) ,因为求出所有的 p i 后, i = 1 n p i   m o d   M 就是我们的答案 x

观察 p i 的限制,可以发现 p i 应该为除了 m i 以外的模数的最小公倍数(因为所有 m i 互质,所以也就是 w i )的倍数,所以我们只需找到一个 p i 使得 p i = k i w i k i 为任意整数),且 p i a i   m o d   m i ,那么这个 p i 就是我们需要的。但是 p i a i   m o d   m i 这个式子并不特殊,我们需要找一些更方便求解的式子来替换它。思考一下可以发现,其实我们只需要找到在满足 p i = k i w i 的条件下,使得 p i 1   m o d   m i 的数,把 p i 乘上 a i ,就得到我们想要的 p i

现在的问题就是如何找到满足条件的 k i ,观察式子 k i w i 1   m o d   m i ,可以看到 k i 实际上是 w i 在模 m i 意义下的逆元,所以:

p i = a i × w i × k i = a i × w i × w i 1

到这里,我们就成功得出了中国剩余定理的公式:

x i = 1 k a i × w i × w i 1 ( m o d   M )

扩展中国剩余定理

 

由于一般情况下模数并不是两两互素,那么之前的做法就行不通了,所以我们就需要用到扩展中国剩余定理。

我们将上面的同余方程组更改一下:

{ x = p 1 m 1 + a 1 x = p 2 m 2 + a 2 x = p n m n + a n

对于这种形式,比较好的办法就是两两合并成一个方程。以合并前两个方程为例:

根据等式可得:

m 1 p 1 + a 1 = m 2 p 2 + a 2

移项:

m 1 p 1 m 2 p 2 = a 2 a 1

由于 p 2 可正可负,为了方便推导,我们变一下号:

m 1 p 1 + m 2 p 2 = a 2 a 1

根据贝祖定理可以知道,若 g c d ( m 1 , m 2 ) a 2 a 1 ,那么整个同余方程无解。

在有解的情况下则可用扩展欧几里得求出一组 x , y 使得:

m 1 x + m 2 y = g c d ( m 1 , m 2 )

两边同时乘上 a 2 a 1 g c d ( m 1 , m 2 ) :

m 1 x ( a 2 a 1 ) g c d ( m 1 , m 2 ) + m 2 y ( a 2 a 1 ) g c d ( m 1 , m 2 ) = a 2 a 1

根据多项式恒等定理:

p 1 = x ( a 2 a 1 ) g c d ( m 1 , m 2 ) , p 2 = y ( a 2 a 1 ) g c d ( m 1 , m 2 )

那么:

m 1 p 1 + a 1 = a 2 m 2 p 2 = x

所以我们目前得到了满足前两个方程的一个 x 的解,
合并后的同余方程为:
x m 1 p 1 + a 1   m o d   l c m ( m 1 , m 2 )

或者也可以是:

x a 2 m 2 p 2   m o d   l c m ( m 1 , m 2 )

若我们要得到满足所有方程的解,那么我们只要把合并后的方程再与其它的方程合并,最后剩下的一个方程就是答案。

应用:【模板】扩展中国剩余定理

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int N=100010;
int n;
LL x,y,lcm,equ[N][2];
LL multi(LL a,LL b,LL p)
{
    a=(a%p+p)%p;b=(b%p+p)%p;LL ans=0;
    for(;a;a>>=1,b=(b*2)%p)if(a&1)ans=(ans+b)%p;
    return ans;
}
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    LL val=exgcd(b,a%b,x,y);
    LL t=x;x=y;y=t-a/b*y;return val;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%lld%lld",&equ[i][1],&equ[i][0]);
    for(int i=1;i<n;i++)
    {
        LL val=exgcd(equ[i][1],equ[i+1][1],x,y);
        lcm=equ[i][1]/val*equ[i+1][1];
        equ[i+1][1]=lcm;
        val=multi(x,(equ[i+1][0]-equ[i][0])/val,lcm);
        equ[i+1][0]=(multi(equ[i][1],val,lcm)+equ[i][0])%lcm;
    }
    printf("%lld\n",(equ[n][0]%equ[n][1]+equ[n][1])%equ[n][1]);
    return 0;
}

注意乘法溢出

其它情况

{ w 1 x a 1   ( m o d   m 1 ) w 2 x a 2   ( m o d   m 2 ) w k x a k   ( m o d   m k )

若未知数 x 的系数不为 1 ,当 w i m i 互质时可以用 e x g c d 求出逆元将 w i 除到另一边,但是如果不互质又该怎么办?我们尝试能不能将其转换成互质的形式。

若要使得 w i m i 互质,我们则需要对方程两边同时除上一个 g c d ( w i , m i ) ,但是这样的正确性和更改后的方程又如何?

Q i = g c d ( w i , m i ) ,那么 w i = Q i l i , m i = Q i r i 。将方程转化为以下形式:

Q i l i × x = a i + Q i r i × q i

其中 q i 为任意整数,那么 a i = Q i ( l i × x + r i × q i ) ,也就是说 a i 必为 Q i 的倍数,若不满足的话则方程组无解,否则我们可以将方程两边同时除以 Q i 得到以下形式:

l i × x = a i + r i × q i

可以发现 l i x a i   m o d   r i w i x a i   m o d   m i

那么我们可以将原方程替换为 l i x a i   m o d   r i ,因为 l i , r i 互质,所以我们就可以用逆元将 l i 除到方程右边,然后就可以用 e x C R T 求解了。

应用:[NOI2018]屠龙勇士

猜你喜欢

转载自blog.csdn.net/hdxrie/article/details/81154544