洛谷 P1495 中国剩余定理

P1495 【模板】中国剩余定理(CRT)/曹冲养猪

 

 

这道题很明显是要用中国剩余定理(孙子定理)来解决。

首先,我们先看看孙子定理是怎么操作的:

 

我们先用一个例子来说明:

在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之  剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。

具体解法分三步:
1.找出三个数:从3和5的公倍数中找出被7除余1的最小数15,从3和7的公倍数中找出被5除余1 的最小数21,最后从5和7的公倍数中找出除3余1的最小数70。
2.用15乘以2(2为最终结果除以7的余数),用21乘以3(3为最终结果除以5的余数),同理,用70乘以2(2为最终结果除以3的余数),然后把三个乘积相加15∗2+21∗3+70∗2得到和233。
3.用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。

那么,我们再求同余方程组的时候,就有了一个公式:

对于这样m1,m2,……,mn 两两互素,且m均为正整数的一组同余方程组

 

我们有这样的一个求根公式:

 

其中,ai 为最终答案除以mi 的余数,Mi 就是 M / mi 的结果 ,而M是所有的m的乘积,Mi-1 是 Mi 模 mi 的逆元

求逆元就可以使用扩展欧几里得的写法来进行求解,这样,这道问题就变得不是那么困难了

上代码:

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

long long n;
long long a[10000],b[10000];

long long exgcd(long long a,long long b,long long &x,long long &y)//扩展欧几里得求逆元 
{
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    
    long long g=exgcd(b,a%b,x,y);//类似于辗转相除法 
    long long tp=x;
    
    x=y;
    y=tp-a/b*y;
    
    return g;
}

long long crt()//中国剩余定理 
{
    long long M=1,r=0;
    
    for(int i=1;i<=n;i++) M*=a[i];//求M 
    for(int i=1;i<=n;i++)
    {
        long long p,y;
        long long m=M/a[i];//求M[i] 
        long long d=exgcd(m,a[i],p,y);//求M[i]的逆元 ,其中 p 为我们所要求解的 x  
        
        r=(r+m*b[i]*p)%M;//套用公式 
    }
    
    if(r<0) r+=M;
    
    return r;
}

int main(void)
{    
    scanf("%lld",&n);
    
    for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
    
    printf("%lld",crt());
    
    return 0;
} 

 

 

 

 

posted @ 2020-05-02 12:38  雾隐  阅读(397)  评论(0编辑  收藏  举报