中国剩余定理及扩展

中国剩余定理

定义: 中国剩余定理其实就是多个线性同于方程的方程组;举个例子,《孙子算经》中曾提到过这样一个问题: 有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何。 其实这就是一个经典的中国剩余定理的例子。

算法流程:

计算所有模的积n -----> 对于第 i 个方程:计算mi = n/ni -------> 计算mi在模ni的意义下的逆元1/mi -------> 计算ci = mi* (mi^-1)(不要对ni取模)得:

[方程为一解为: ans = sum_{i=1}^{k}a_ic_i (mod n) ]

代码实现:

//式中m[i]为除数必须两两互质才能这么算,否则请看excrt算法
//式中b[i]是除数, a[i]是余数
void exgcd(int a,int b,int &x,int &y)
{
    if(b==0){ x=1; y=0; return;}
    exgcd(b,a%b,x,y);
    int tp=x;
    x=y; y=tp-a/b*y;
}

int china()
{
    int ans=0,lcm=1,x,y;
    for(int i=1;i<=k;++i) lcm*=b[i];		//计算n
    for(int i=1;i<=k;++i)
    {
        int tp=lcm/b[i];					//求m[i]
        exgcd(tp,b[i],x,y);					
        x=(x%b[i]+b[i])%b[i];				//x要为最小非负整数解
        ans=(ans+tp*x*a[i])%lcm;			//ai*ci ci=tp*x
    }
    return (ans+lcm)%lcm;
}

扩展中国剩余定理

它与中国剩余定理不同之处在于:中国剩余定理中所有的模一定是两两互素的,二在excrt中:所有的模不一定两两互素。

思路为求解k次exgcd即可:

//a[i]和b[i]分别是余数和除数

LL mul(LL a, LL b, LL mod){
    LL ans = 0 % mod;
    while(b){
        if(b & 1)	ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
	}
    return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
    if(b == 0)	{x = 1; y = 0; return a;}
    LL gcd = exgcd(b, a % b, x, y);
    LL tp = x; x = y; y = tp - a / b * y;
    return gcd;
}
LL excrt()
{
    LL m = b[1], ans = a[1];			//第1个的答案,然后往下递推
    for(int i = 2; i <= n; i++)
    {
        LL c = (a[i] - ans % b[i] + b[i]) % b[i];
        LL gcd = exgcd(m, b[i], x, y);
        if(c % gcd != 0) 	return -1; 		//无解
    	x = mul(x, c/gcd, b[i]);
   	 	ans += x * m;
    	m *= b[i] / gcd;
    	ans = (ans % m + m) % m;
	}
	return (ans % m + m) % m;
}

洛谷excrt模板题:

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
const int inf = 0x3f3f3f3f;
int gcd(int a,int b)    { return b == 0 ? a : gcd(b, a % b); }

LL n, a[maxn], b[maxn];
LL x, y;
LL mul(LL a, LL b, LL mod){
    LL ans = 0 % mod;
    while(b){
        if(b & 1)	ans = (ans + a) % mod;
        a = (a + a) % mod;
        b >>= 1;
	}
    return ans % mod;
}
LL exgcd(LL a, LL b, LL &x ,LL &y)
{
    if(b == 0)	{x = 1; y = 0; return a;}
    LL gcd = exgcd(b, a % b, x, y);
    LL tp = x; x = y; y = tp - a / b * y;
    return gcd;
}
LL excrt()
{
    LL m = b[1], ans = a[1];			//第1个的答案,然后往下递推
    for(int i = 2; i <= n; i++)
    {
        LL c = (a[i] - ans % b[i] + b[i]) % b[i];
        LL gcd = exgcd(m, b[i], x, y);
        if(c % gcd != 0) 	return -1; 		//无解
    	x = mul(x, c/gcd, b[i]);
   	 	ans += x * m;
    	m *= b[i] / gcd;
    	ans = (ans % m + m) % m;
	}
	return (ans % m + m) % m;
}

int main()
{
    scanf("%lld", &n);
    for(int i = 1; i <= n; ++i){
        scanf("%lld %lld", &b[i], &a[i]);       //除数和余数
    }
    printf("%lld
", excrt());
    system("pause");
}
原文地址:https://www.cnblogs.com/StungYep/p/12252598.html