中国剩余定理模板

/*
    中国剩余定理简单解释:
    定理:如果a%b=c   那么(a+kb)%b=c

    首先看一个简单例子:对于x%3=2,x%5=3,x%7=2,求解x(最小)
    令m[i]=3,5,7  a[i]=2,3,2;
    我们假设n1%3=2,但是又要满足另外两个方程的解,那么n1必须是5,7的倍数
    同理n2%3=2且n2必须是3,7的倍数,n3%7=2且n3必须是3,5的倍数
    那么(n1+n2+n3)就是满足这三个同余方程的一个解
    因此x=(n1+n2+n3)%M
    那么如何求n1,n2,n3呢?可通过欧几里得算法求解
    令M=LCD(m[i])即这三个数的最小公倍数,又因为它们互质所以M=m[i]*m[2]*m[3];
    Ni=M/m[i],那么ni=Ni*t使得Ni*t%m[i]=a[i]
    得到方程Ni*t+m[i]*y=a[i],t,y为要求的变量
    ni=Ni*t*a[i],因为通过欧几里得算出来的t只满足Ni*t%m[i]=1,因此还需要乘以a[i]

*/



#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
//欧几里得算法
__int64 extend_euclid(__int64 a,__int64 b,__int64 &x,__int64 &y)
{
    if(b==0)
    {
        x=1;y=0;
        return a;
    }
    __int64 d = extend_euclid(b,a%b,x,y);
    __int64 t = x;
    x=y;
    y=t-a/b*y;
    return d;
}
//中国剩余定理求解x=a[i](mod(m[i]))---也就是x%m[i]=a[i],只针对m[i]互质情况
//参数len,同余方程的个数,m[i],a[i]从1开始,int类型根据题意更改
__int64 cnt(int len,__int64 m[],__int64 a[])
{
    __int64 M = 1,ans = 0;
    for(int i=1;i<=len;i++)
        M*=m[i];
    __int64 x,y;
    for(int i=1;i<=len;i++)
    {
        __int64 Ni = M/m[i];
        extend_euclid(Ni,m[i],x,y);
        x=(x%m[i]+m[i])%m[i];//保证x为最小的正数解
        ans=(ans+x*a[i]*Ni)%M;
    }
    if(ans<0)
        ans=ans+M;
    return ans;
}
int main()
{
    int n;
    __int64 m[10],a[10];
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%I64d%I64d",&m[i],&a[i]);
    printf("%I64d\n",cnt(n,m,a));
    return 0;
}

 

posted on 2016-08-16 09:50  wastonl  阅读(240)  评论(0编辑  收藏  举报