Always an integer UVALive - 4119 總是整數

題目鏈接

組合數學主要研究計數問題。比如,從n 個人中選兩個人有多少種方法?圓周上有n個點,兩兩相連之後最多能把圓面分成多少部分?如圖2-16 所示。有一個金字塔,從塔頂開始每一層分別有1 x 1 , 2x2, ..., nXn 個小立方體,問一共有多少個小立方體?

很多問題的答案都可以寫成n 的簡單多項式。比如上述第一個問題的答案是n(n一1)/2 ,也就是(n^2 -n)/2 ; 第二個問題的答案是(n^4-6n^3 +23n2一18n+24)/24 ; 第三個問題的答案是n(n+ 1 )(2n+ 1 )/6 ,即(2n^3 +2n^2+n)/6 。由於上述3 個多項式是計數問題的答案,因此當n 取任意正整數時,這些多項式的值都是整數。當然,對於其他多項式,這個性質並不一定成立。給定一個形如P/D (其中P 是n 的整係數多項式, D 是正整數)的多項式,判斷它是否在所有正整數處取到整數值。

【分析】
本題實際上是判斷一個整係數多項式P 的值是否總是正整數D 的倍數。一個容易想到的方法是,隨機代入很多整數計算P/D , 如果全都是整數,那麼很有可能是"Always aninteger" ;如果有的不是整數,那麼答案必然是"Not always an integer" 。這個方法看起來有些投機取巧,但效果非常不錯。事實上,不需要隨機代入,只需要
把n=l , 2, 3, ..., k-t: l 全試一遍就可以了,其中k 是多項式中最高項的次數。爲什麼可以這樣做呢?讓我們從k 較小的情況開始研究。

  1. 當k=0 時, P 里根本就沒有n 個變量,所以只需代入P(l)計算即可。
  2. 當k= l 時, P 是n 的一次多項式,設爲an+b , 則P(n+l)-P(n )=a。如果把P(n)看成一個數列的第n J頁,則{P(n)} 是一個首項爲P(l) , 公差爲整數。的等差數列,因此只要首項和公差均爲D 的倍數,整個數列的所有項都會是D 的倍數。因此只需驗證P(l)和P(2) 。
  3. 當k=2 時, P 是n 的二次多項式,設爲αn2+bn+c , 則P(n+1)-P(n )=2an+α+b 。注意到這個2an+α+b 是n 的一次多項式,根據剛纔的結論,只要n=1 和n=2 時它都是D 的倍數,對於所有正整數n , 它都將是D 的倍數。這樣,相鄰兩項的差爲D 的倍數,再加上首項也爲D 的倍數,則P(n)將總是D 的倍數。整理一下,只要P(1), P(2)-P(1), P(3)-P(2)都是D 的倍數即可。這等價於驗證P(1) , P(2)和P(3) 。

看到這裏,結論己經不難猜到了。對於k 次多項式P(時,相鄰兩項之差P(n+1)-P(n)是關於n 的k- l 次多項式,根據數學歸納法,命題得證。順便說一句,數列dP(n)=P(n+ l)-P(n)稱爲P(n) 的差分數列(difference series) 。而差分數列的差分數列爲二階差分數歹ú cfp(時,依此類推。這樣, k=2 的證明可以用圖2-17 說明。二次多項式P(2) P(3) P(4) P(5) '" / '" / "' ./ -次多項式dP(2) dP(3) dP(4)
'" / '" / 常數d'-P(2) d'-P(3)

如果P(1), P(2), P(3)都是D 的倍數,意味着P(1), dP(1)和cfp(1)都是D 的倍數。由於第二行(即所有的cfP(n)) 是常數,所以整個第三行都是D 的倍數:根據差分的定義,可以推導出整個第二行都是D 的倍數,再進一步得到第一行也都是D 的倍數。

#include <iostream>
#include <string> 
#include <cctype>
#include <vector>
#include <cstdlib>
using namespace std;

struct Polynomial{
	vector<int> a, p;  // 第i項爲a[i] * n^p[i]
	void perse(string s){ // 解析多項式,多取一個)減少取數時的越界判斷
		for(int i = 0, len = s.length(); i < len-1;){ // 每次循環體解析一個a*n^p
			int sign = 1;
			if(s[i] == '-'){
				sign = -1;
				i++;
			}
			if(s[i] == '+') i++;
			int v = 0;
			while(isdigit(s[i]))
				v = v*10 + s[i++]-'0';			
			if(v == 0) v = 1; //無係數,按1處理
			v *= sign;							
			a.push_back(v);
			v = 1; //無指數默認爲1 
			if(s[++i] == '^'){
				i++;
				v = 0; // 有指數 
				if(s[i] == '-' ){ // 有指數項
					sign = -1;
					i++;
				} 
				while(isdigit(s[i]))
					v = v*10 + s[i++] - '0';
			}
			p.push_back(v);
		}
	}	
	int mod(int x,int MOD){
		int ans = 0;
		for(int i = 0; i < a.size(); i++){
			int m = a[i];
			for(int j = 0; j < p[i]; j++)
				m = ((long long)m * x) % MOD; // 注意避免溢出
			ans = ((long long)ans + m) % MOD; // 加法也可能會溢出!
		}
		return ans;
	}
};

bool check(string s){
	Polynomial p;
	int loc = s.find('/');
	p.perse(s.substr(1, loc-1)); //多取一個)防止越界判斷 
	int D = atoi(s.substr(loc+1).c_str());
	for(int i = 1; i <= p.p[0]+1; i++)
		if(p.mod(i,D) != 0) return false;
	return true;
}

int main(int argc, char** argv) {
	string s;
	int kase = 0;
	while(cin>> s && s[0] != '.'){	
		if(check(s))	printf("Case %d: Always an integer\n", ++kase);
		else printf("Case %d: Not always an integer\n", ++kase);
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章