51Nod 1079中国剩余定理(孙子定理)

一个正整数K,给出K Mod 一些质数的结果,求符合条件的最小的K。例如,K % 2 = 1, K % 3 = 2, K % 5 = 3。符合条件的最小的K = 23。
Input
第1行:1个数N表示后面输入的质数及模的数量。(2 <= N <= 10)
第2 - N + 1行,每行2个数P和M,中间用空格分隔,P是质数,M是K % P的结果。(2 <= P <= 100, 0 <= K < P)
Output
输出符合条件的最小的K。数据中所有K均小于10^9。
Sample Input
3
2 1
3 2
5 3
Sample Output
23

中国剩余定理讲解:http://www.cnblogs.com/freinds/p/6388992.html

题意:

解题思路:

(刚开始学习C#,不小心找到了这个题的C#代码就顺便把这个知识点理解理解~)
定理1:几个数相加,如果存在一个加数,不能被数a整除,那么它们的和,就不能被整数a整除。

定理2:两数不能整除,若除数扩大(或缩小)了几倍,而被除数不变,则其商和余数也同时扩大(或缩小)相同的倍数(余数必小于除数)。

现给出求解该问题的具体步骤:

1、求出最小公倍数

lcm=3*5*7=105

2、求各个数所对应的基础数

(1)105÷3=35

35÷3=11……2 //基础数35

(2)105÷5=21

21÷5=4……1

定理2把1扩大3倍得到3,那么被除数也扩大3倍,得到21*3=63//基础数63

3、105÷7=15

15÷7=2……1

定理2把1扩大2倍得到2,那么被除数也扩大2倍,得到15*2=30//基础数30

把得到的基础数加和(注意:基础数不一定就是正数)

35+63+30=128

4、减去最小公倍数lcm(在比最小公倍数大的情况下)

x=128-105=23

那么满足题意得最小的数就是23了。

先给出模板:

#include<stdio.h>

int a[15],m[15];//m[i]是除数,a[i]是余数 

void extgcd(int a,int b,int &x,int &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    extgcd(b,a%b,x,y);
    int t=x;
    x=y;
    y=t-(a/b)*y;
}

int crt(int a[],int m[],int n)
{
    int M=1,ans=0,x,y;
    for(int i=0;i<n;i++)
        M*=m[i];//这几个数相乘 
    for(int i=0;i<n;i++)//对每个数处理 
    {
        int Mi=M/m[i]; 
        extgcd(Mi,m[i],x,y);//得到符合的解x,y 
        ans=(ans+Mi*x*a[i])%M;//将每一步的ans累加 
    }
    return (ans+M)%M;//也可写成两句话 if(ans<0) ans+=M; return ans;目的是转化为最小整数 
}

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

AC代码:

#include<stdio.h>
#define ll long long 

ll a[15],m[15];//m[i]是除数,a[i]是余数 

void extgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    extgcd(b,a%b,x,y);
    ll t=x;
    x=y;
    y=t-(a/b)*y;
}

int crt(ll a[],ll m[],int n)
{
    ll M=1,ans=0,x,y;
    for(int i=0;i<n;i++)
        M*=m[i];//这几个数相乘 
    for(int i=0;i<n;i++)//对每个数处理 
    {
        ll Mi=M/m[i]; 
        extgcd(Mi,m[i],x,y);//得到符合的解x,y 
        ans=(ans+Mi*x*a[i])%M;//将每一步的ans累加 
    }
    return (ans+M)%M;//也可写成两句话 if(ans<0) ans+=M; return ans;目的是转化为最小整数 
}

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

来个C#代码= =

using System;
using System.IO;
using System.Numerics;

namespace timeless
{
    class Program
    {
        public static long extend_gcd(long a,long b,ref long x,ref long y)
        {
            if(a==0&&b==0) return -1;
            if(b==0)
            {
                x=1;
                y=0;
                return a;
            }
            long d=extend_gcd(b,a%b,ref y,ref x);
            y-=a/b*x;
            return d;
        }
        static void Main(string[] args)
        {
            StreamReader sr=new StreamReader(Console.OpenStandardInput());
            StreamWriter sw=new StreamWriter(Console.OpenStandardOutput());
            int n=int.Parse(sr.ReadLine());
            long M=1;
            long[] p=new long[15];
            long[] m=new long[15];
            string[] str;
            for(int i=0;i<n;++i)
            {
                str=sr.ReadLine().Split(' ');
                p[i]=int.Parse(str[0]);
                m[i]=int.Parse(str[1]);
                M=M*p[i];
            }
            long pi=0,qi=0;
            long res=0;
            for(int i=0;i<n;++i)
            {
                long Mi=M/p[i];
                extend_gcd(Mi,p[i],ref pi,ref qi);
                res=(res+Mi*pi*m[i])%M;
            }
            sw.WriteLine((res%M+M)%M);
            sw.Flush();
            sw.Close();
            sw.Close();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值