同餘定理——簡單理解與使用

同餘對於防止數的溢出和對某些題有着特殊的作用,博主學的也不深,就對簡單的同餘應用進行一個分享。(中間的乘號不是我不打,是這個乘號會將文章佈局弄亂,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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章