中国剩余定理 【CRT】 【记录】

问题一:给你n个方程组:

x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,其中m[]必须两两互质。


直接exgcd可以KO

LL gcd(LL a, LL b){
    return b == 0 ? a : gcd(b, a%b);
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
    if(!b){d = a, x = 1, y = 0;}
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x * (a / b);
    }
}
//l-r为方程组下标 方程 x % m = a
LL CRT(LL l, LL r, LL *m, LL *a)
{
    LL M = 1, d, y, x = 0;
    for(LL i = l; i <= r; i++)
        M =  M / gcd(M, m[i]) * m[i];
    for(LL i = l; i <= r; i++)
    {
        LL w = M / m[i];
        exgcd(m[i], w, d, d, y);
        x = (x + y * w * a[i]) % M;
    }
    return (x + M) % M;
}




问题二:给你n个方程组:

x%m[0] = a[0]
x%m[1] = a[1]
···
x%m[n-1] = a[n-1]
求变量x 的值,无解输出-1。其中m[]的值没有要求。


这时需要用到合并方程的做法。因为exgcd只能求解一个方程a * x + b * y = c的解,我们需要把n个方程合并成一个。


合并方程的讲解如下:来自九野的博客



问题描述:给出bi,ni的值,且n1, n2, n3,…, ni两两之间不一定互质,求Res的值? 
解:采用的是合并方程的做法。 
这里将以合并第一第二个方程为例进行说明
 
由上图前2个方程得(设k1、k2为某一整数):
 

中国剩余定理 【CRT】 【记录】_第1张图片

所以我们简化一下结论:

已知方程组(b1,b2,n1,n2是已知量):

res%b1 = n1

res%b2 = n2

->

合并两条方程得到:

res % ( (n1*n2)/d ) = b1+n1*( K%(n2/d))

其中k = (k1*(b2-b1)/d) % (n2/d); 

其中d = gcd(n1,n2);

其中k1:

k1*n1 - k2*n2 = b2-b1

k1, d 可以直接由extend_gcd得到 extend_gcd(n1,n2,d,k1,k2);

(b2-b1)%d == 0 说明extend跑出的k1是一个解,否则说明不存在满足解的k1

注意求k时:为了得到最小非负整数k,所以用一个取模的技巧

k = (k%mod+mod)%mod, 其中mod = m[i] / d,每一次的mod值可能不一样;



实现:

LL gcd(LL a, LL b){
    return b == 0 ? a : gcd(b, a%b);
}
void exgcd(LL a, LL b, LL &d, LL &x, LL &y)
{
    if(!b){d = a, x = 1, y = 0;}
    else
    {
        exgcd(b, a%b, d, y, x);
        y -= x * (a / b);
    }
}
LL CRT(LL l, LL r, LL *m, LL *a)//方程 x % m = a
{
    LL lcm = 1;
    for(LL i = l; i <= r; i++)
        lcm = lcm / gcd(lcm, m[i]) * m[i];
    for(LL i = l+1; i <= r; i++)//合并方程 共需r-l+1次
    {
        LL A = m[l], B = m[i], d, x, y, c = a[i] - a[l];//合并方程后 各个系数
        exgcd(A, B, d, x, y);//d = gcd(A, B);
        if(c % d)//对扩展后的式子a*x + b*y = c 当且仅当c % d == 0是才有解
            return -1;
        LL mod = m[i] / d;
        LL k = ((x * c / d) % mod + mod) % mod;
        a[l] = m[l] * k + a[l];
        m[l] = m[l] * m[i] / d;//更新方程系数 利用该方程继续和下一个方程合并
    }
    if(a[l] == 0) return lcm;//特判
    return a[l];//合并到最后a[l] 就是解
}


你可能感兴趣的:(中国剩余定理 【CRT】 【记录】)