中国剩余定理总结

                                                                                                                              中国剩余定理总结


  当你遇到x == c (mod p) 要你求解x的时候,是不是很容易的想到了这样转换---> x - py = c运用extgcd得到答案。但是现在如果有很多个x呢?如:

                                          x == c1(mod p1)

                                          x == c2(mod p2)

                                           ...............................

                                         x == cn(mod pn)

   这时候你要如何求解呢?肯定不能像上面一样吧!

 其实这类问题有一个专门的算法成为孙子定理流行叫法是中国剩余定理(China Remainder Theorem)。

一般一个算法的出现肯定有其的约束条件吧?对啊。这个普通版的CRT的限制是模之间是两两互素的。但是不互难道就不能求解了吗?当然也可以,这时候就要用CRT的扩展版。下面分别举例来说明和训练。


一、当模两两互质的条件下

题目链接:Click Here~

分解出题目的意思:现在假设总共有X 头猪,每个猪圈Y头猪。

那么显然可以得到 ---> X - Y*ai = bi 根据同余式的定义对式子转换---> X == bi(mod ai)。结果很裸了。套模板的时候。


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

typedef long long LL;
const int MAXN = 10 + 10;
int n,a[MAXN],m[MAXN];

void extgcd(LL a,LL b,LL& d,LL& x,LL& y){
    if(!b) { d = a; x = 1; y = 0; }
    else { extgcd(b,a%b,d,y,x); y -= x*(a/b); }
}

LL china(int n,int *a,int *m){
   LL M = 1,d,y,x = 0;
   for(int i = 0;i < n;++i) M *= m[i];
   for(int i = 0;i < n;++i){
      LL w = M / m[i];
      extgcd((LL)m[i],w,d,d,y);
      x = (x + y*w*a[i]) % M;
   }
   return (x+M)%M;
}

int main()
{
    while(~scanf("%d",&n)){
        for(int i = 0;i < n;++i)
            scanf("%d%d",&m[i],&a[i]);
        printf("%I64d\n",china(n,a,m));
    }
    return 0;
}

 

二、当模两两不互质

题目链接:Click Here~  

题目很简单就不解释了。最要来看看如何转换成CRT来求解。现在我们假设每堆硬币都有Y个,则可以得到以下等式:1*X - Y*Mi = Ai ---> X == Ai(mod Mi) 下面就是模板问题了。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

typedef long long LL;
int n,times;

LL A[MAXN],r[MAXN];   //x == Ai(mod ri)

LL gcd(LL a,LL b){
   return b?gcd(b,a%b):a;
}

void ex_gcd(LL a,LL b,LL& d,LL& x,LL& y){
    if(!b) { d = a; x = 1; y = 0; }
    else { ex_gcd(b,a%b,d,y,x); y -= x*(a/b); }
}

void read_case(){
    scanf("%d",&n);
    lcm = 1;
    for(int i = 1;i <= n;++i){
        scanf("%lld",&A[i]);
        lcm = lcm / gcd(lcm,A[i]) * A[i];
    }
    for(int i = 1;i <= n;++i) scanf("%lld",&r[i]);
}

void solve(){
   read_case();
   
   LL lcm;
   LL a,b,c,d,x,y;
   for(int i = 2;i <= n;++i){
       a = A[1],b = A[i],c = r[i] - r[1];
       ex_gcd(a,b,d,x,y);
       if(c % d){printf("-1\n"); return;}
       LL b1 = b / d;
       x *= c / d;
       x = (x % b1 + b1) % b1;
       r[1] = A[1] * x + r[1];
       A[1] = A[1]*(A[i] / d);
   }
   if(r[1] == 0) printf("%lld\n",lcm);
   else printf("%lld\n",r[1]);
}

int main(){
    int T;
    times = 0;
    scanf("%d",&T);
    while(T--){
       printf("Case %d: ",++times);
       solve();
    }
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值