同余定理——简单理解与使用

同余对于防止数的溢出和对某些题有着特殊的作用,博主学的也不深,就对简单的同余应用进行一个分享。(中间的乘号不是我不打,是这个乘号会将文章布局弄乱,blog缺陷)
先列举几个公式
在此之前,先证明一个公式:如果a%n=b%n,就说a与b同余,则(a-b)%n=0。也就是说a-b是n的整数倍。为什么呢?
你想想,如果a%n=0,则a=a%n+(a/n)n,后面一项的/号是整除,也就是说任意一个数能分为取余部分和整倍数部分。
则a=a%n+a1n(a1为整数),b=b%n+b1n(b1为整数),b-a中,取余部分相同,最终为(b1-a1)n,a1,b1都为整数,所以b1-a1实际上可以取任意整数,所以(b1-a1)n为n的倍数,所以b-a为n的倍数。
实际上,前面已经说了a=a%n+a1n(a1为整数)实际上就已经证明出来了。所以以后就可以直接用,如果某个数与a同余则那个数b=a+tn(t为任意整数,n为同余的那个数),当然这个结论是充分必要的,可以反着用。
下面推出几个推论
1:(a+b)%n=((a%n)+(b%n))%n;
将a,b分解为取余部分和整除部分,由同余定理的逆用,可知a%n=(a-tn)%n。当然,数学还有一个任意替代原理,就是说你a可以换为a+b,a+b+c,ab…就是你要把a看为一个整体,怎么换都可以,而(a%n)+(b%n)是不是相当于a-(a/n)n+b-(b/n)n=a+b-(a/n+b/n)n=a+b-tn。(/号为整除,a+b看为整体)
2:当然你们可能也发现了,上面证明过程实际上还是有一点点问题,就是说还可能是负数对吧,我先说一下,上面要求结果都是正数,当然t可以是负数。博主查了一下资料,由于不同机器实现等原因,负数取模还没有一个可靠的概率,可能有,我没查到。但大家建议都用正数啊。
(a-b)%n=((a%n)-(b%n)+n)%n 这个式子我就不来证明了,就说一下由于(a%n)-(b%n)可能为负数当然负数小于n,所以加上n。
3:ab%n=((a%n)(b%n))%n
如果ab都小于n,相当于什么都没做,自然成立,如果a,b有大于n的,则a=a1+t1n,b=b1+t2n,相乘之后有a1*b1+…后面都是n的倍数,直接可以减掉。
以上三个式子加上任意替换原则,可以得出:
要计算只包含加法,减法和乘法的整数表达式除以正整数n的余数,可以在每步计算之后对n取余,结果不变。
可以干嘛,可以防溢出,还有其他作用。
下面是一道例题:
**给出正整数 n 和 m,统计满足以下条件的正整数对 (a,b) 的数量:

  1. 1≤a≤n,1≤b≤m;
  2. a×b 是 2016 的倍数。
    Input
    输入包含不超过 30 组数据。
    每组数据包含两个整数 n,m (1≤n,m≤10 9).
    Output
    对于每组数据,输出一个整数表示满足条件的数量。
    Sample Input
    32 63
    2016 2016
    1000000000 1000000000
    Sample Output
    1
    30576
    7523146895502644**
    详解在代码中
#include<cstdio>
#include<cstring>
#define ll long long
ll minx(ll a,ll b){
	return a<b?a:b;
}
/*
这题看数据很可能会爆int,用long long,
看数据多少,用枚举肯定会超时。 
*/
int main()
{
	ll n,m,ans=0,i,j,x,y;
	/*
	我解释一下,如果a*b%2016=0
	则(a+t1*2016)*(b+t2*2016)%2016=0,你展开一下就可以发现成立。 
	为什么我上面那三个公式的证明过程写的那么详细,
	因为实际上根据那样的方法可以证明出各种不一样的公式
	但原理就那些,要学会举一反三 。 
	*/
	while((scanf("%lld %lld",&n,&m))==2)
	{
		ans=0;
		/*
		所以实际上,你求一下在1到2016之间有多少个整数对i,j满足
		相乘为2016的倍数,然后看一下对于每队满足条件的i,j这两个数分别可以加上多少个2016 。 
		即设在1到n中,有存在n个t整数值满足x=i+2016*t,
		同理求y,相乘个数。
		最终再加起来即可 
		*/
		for(i=1;i<=minx(n,2016);i++){                
			for(j=1;j<=minx(m,2016);j++){
				if((i*j)%2016==0){
					x=n/2016;
					y=m/2016;
					if(n%2016>=i)x++;     //因为整除可能有损失,比如对于i,如果n=i+1+2016k,那就有k+1个
					if(m%2016>=j)y++;     // 如果 n=i-1+2016k,那只有k个 
					ans+=x*y;
				}
			}
		}
		printf("%lld\n",ans);
	}
}
发布了14 篇原创文章 · 获赞 16 · 访问量 1283

猜你喜欢

转载自blog.csdn.net/weixin_45981189/article/details/105024754
今日推荐