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;
}

 

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