ACM_模板_中国剩余定理(互质与非互质)

中国剩余(余数)定理,又称孙子定理,用于求解一组同余方程,在讲解之前还是举个例子:x==(2,3,2) mod (3,5,7) 求解x
中国剩余定理x1==(1,0,0);x2==(0,1,0);x3=(0,0,1)
x==2x1+3x2+2x3.

说的通俗一点就是一个x,除3余2,除5余3,除7余2,很明显x+lcm(3,5,7)都是这个方程的解,所以显然我们是求解最小的这个数,这个求法就是先求出一个x1,除3余1,除5余0,除7余0;找一个x2,除3余0,除5余1,除7余0;找一个x3,除3余0,除5余0,除7余1。我们要求的x = 2*x1+3*x2+2*x3,当然这个x并不一定是最小的,我们可以将其对lcm(3,5,7)取一下模即可。

这里小编不给证明(其实我也不是很清楚),这里小编就给大家看一下代码实现的这个过程,在求解1mod m的时候相当于求解逆元,我们可以用扩展欧几里德定理求解(不懂的可以点开看小编的另一篇关于这个的详解)。

此算法的模板还要分两种,被除数是否互质。

互质:

#include <cstdio>//互质
int exGcd(int a,int b,int &x,int &y)
{
	if(b == 0)
	{
		x = 1,y = 0;
		return a;
	}
	int d = exGcd(b,a%b,y,x);
	y -= a/b*x;
	return d;
}
int Chinese_Remainder(int mod[],int prime[],int len)
{
	int i,d,x,y,m,n,ret;
	ret = 0,n = 1;
	for(i=0; i<len; i++) n *= prime[i];
	for(i=0; i<len; i++)
	{
		m = n/prime[i];
		d = exGcd(prime[i],m,x,y);
		ret = (ret+y*m*mod[i])%n;
	}
	return (n+ret%n)%n;
}
int main()
{
	int n,i;
	int mod[15],prime[15];
	while(scanf("%d",&n)&&n)
	{
		for(i=0; i<n; i++)
			scanf("%d%d",&prime[i],&mod[i]);
		printf("%d\n",Chinese_Remainder(mod,prime,n));
	}
	return 0;
}


非互质:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define LL __int64
const LL maxn=20;
//拓展欧几里得定理,求ax+by=gcd(a,b)的一组解(x,y),d=gcd(a,b)
void exGcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b){d=a;x=1;y=0;}
    else{exGcd(b,a%b,d,y,x);y-=x*(a/b);}
}
LL Chinese_Remainder(LL n,LL a[],LL b[])
{
    LL m1,r1,m2,r2,flag=0,i,d,x,y,c,t;
    m1=a[0],r1=b[0];
    flag=0;
    for(i=1;i<n;i++)
    {
        m2=a[i],r2=b[i];
        if(flag)continue;
        exGcd(m1,m2,d,x,y);//d=exGcd(m1,m2);x*m1+y*m2=d;
        c=r2-r1;
        if(c%d)//对于方程m1*x+m2*y=c,如果c不是d的倍数就无整数解
        {
            flag=1;
            continue;
        }
        t=m2/d;//对于方程m1x+m2y=c=r2-r1,若(x0,y0)是一组整数解,那么(x0+k*m2/d,y0-k*m1/d)也是一组整数解(k为任意整数)
                //其中x0=x*c/d,y0=x*c/d;
        x=(c/d*x%t+t)%t;//保证x0是正数,因为x+k*t是解,(x%t+t)%t也必定是正数解(必定存在某个k使得(x%t+t)%t=x+k*t)
        r1=m1*x+r1;//新求的r1就是前i组的解,Mi=m1*x+M(i-1)=r2-m2*y(m1为前i个m的最小公倍数);对m2取余时,余数为r2;
                    //对以前的m取余时,Mi%m=m1*x%m+M(i-1)%m=M(i-1)%m=r
        m1=m1*m2/d;
    }
    if(flag)return -1;
    if(n==1&&r1==0)return m1;//结果不能为0
    return r1;
}
int main()
{
    LL T,i,n,tt=0;
    LL a[maxn],b[maxn];
    cin>>T;
    while(T--)
    {
        cin>>n;
        for(i=0;i<n;i++)
            cin>>a[i];
        for(i=0;i<n;i++)
            cin>>b[i];
        cout<<"Case "<<++tt<<": "<<Chinese_Remainder(n,a,b)<<endl;
    }
    return 0;
}




  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大家都是抱着对算法与数据结构极大的兴趣才参加集训的,我们也希望大家学有所成,但是刚刚接触信息学领域的同学往往存在很多困惑,不知道从何入手学习,在这篇向导里,我希望能将自己不多的经验与大家分享,希望对各位有所帮助. 一、语言是最重要的基本功 无论侧重于什么方面,只要是通过计算机程序去最终实现的竞赛,语言都是大家要过的第一道关.亚洲赛区的比赛支持的语言包括C/C++与JAVA.虽然JAVA在应用极为广泛,但是其运行速度不可恭维.而且在以往的比赛中来看,大多数队伍还是采用了C或者C++.而且C语言是大家接触的第一门编程语言,所以我们集训队都采用C和C++混编的方式写代码. 新来的同学可能C的基础知识刚刚学完,还没有接触过C++,其实在赛场上使用纯C的选手还是大有人在的,它们主要是看重了纯C在效率上的优势,所以这部分同学如果时间有限,并不需要急着去学习新的语言,只要提高了自己在算法设计上的造诣,纯C一样能发挥巨大的威力.但是我还是希望大家都能够学点C++. C++相对于C,在输入输出流上的封装大大方便了我们的操作,同时降低了出错的可能性,并且能够很好地实现标准流与文件流的切换,方便了调试的工作。如果有些同学比较在意这点,可以尝试C和C++的混编,毕竟仅仅学习C++的流操作还是不花什么时间的。 C++的另一个支持来源于标准模版库(STL),库中提供的对于基本数据结构的统一接口操作和基本算法的实现可以缩减我们编写代码的长度,这可以节省一些时间。但是,与此相对的,使用STL要在效率上做出一些牺牲,对于输入规模很大的题目,有时候必须放弃STL,这意味着我们不能存在“有了STL就可以不去管基本算法的实现”的想法;另外,熟练和恰当地使用STL必须经过一定时间的积累,准确地了解各种操作的时间复杂度,切忌对STL中不熟悉的部分滥用,因为这其中蕴涵着许多初学者不易发现的陷阱。像STL中的很多容器, vector,queue,stack,map,set等一定要比较熟悉,STL中的sort是必需要掌握的.掌握这些STL知识后写代码的时候相对于纯C会节省不少时间. C语言学习推荐:C程序设计(谭浩强编著) C++学习推荐: C++Prime, C++大学教程.(其实基本上的C++教程都行的…) STL学习推荐: C++Prime,STL标准库.(理论联系实际,边学就用学的最快) 二、以数学为主的基础知识十分重要 虽然被定性为程序设计竞赛,但是参赛选手所遇到的问题更多的是没有解决问题的思路,而不是有了思路却死活不能实现,这就是平时积累的基础知识不够。竞赛中对于基础学科的涉及主要集中于数学,此外对于物理、电路等等也可能有一定应用,但是不多。因此,大一的同学也不必为自己还没学数据结构而感到不知从何入手提高,把数学捡起来吧!下面来谈谈在竞赛中应用的数学的主要分支。 1、离散数学——作为计算机学科的基础,离散数学是竞赛中涉及最多的数学分支,其重中之重又在于图论和组合数学,尤其是图论。 图论之所以运用最多是因为它的变化最多,而且可以轻易地结合基本数据结构和许多算法的基本思想,较多用到的知识包括连通性判断、DFS和BFS,关节点和关键路径、欧拉回路、最小生成树、最短路径、二部图匹配和网络流等等。虽然这部分的比重很大,但是往往也是竞赛中的难题所在,如果有初学者对于这部分的某些具体内容暂时感到力不从心,也不必着急,可以慢慢积累。 竞赛中设计的组合计数问题大都需要用组合数学来解决,组合数学中的知识相比于图论要简单一些,很多知识对于小学上过奥校的同学来说已经十分熟悉,但是也有一些部分需要先对代数结构中的群论有初步了解才能进行学习。组合数学在竞赛中很少以难题的形式出现,但是如果积累不够,任何一道这方面的题目却都有可能成为难题。 2、数论——以素数判断和同余为模型构造出来的题目往往需要较多的数论知识来解决,这部分在竞赛中的比重并不大,但只要来上一道,也足以使知识不足的人冥思苦想上一阵时间。素数判断和同余最常见的是在以密码学为背景的题目中出现,在运用密码学常识确定大概的过程之后,核心算法往往要涉及数论的内容。 3、计算几何——计算几何相比于其它部分来说是比较独立的,就是说它和其它的知识点很少有过多的结合,较常用到的部分包括——线段相交的判断、多边形面积的计算、内点外点的判断、凸包等等。计算几何的题目难度不会很大,但也永远不会成为最弱的题。 4、线性代数——对线性代数的应用都是围绕矩阵展开的,一些表面上是模拟的题目往往可以借助于矩阵来找到更好的算法。 5、概率论——竞赛是以黑箱来判卷的,这就是说你几乎不能动使用概率算法的念头,但这也并不是说概率就没有用。关于这一点,只有通过一定的练习才能体会。而且近年来概率题出现的次数越来越多了.

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值