南邮 OJ 1404 取数游戏

取数游戏

时间限制(普通/Java) :  1000 MS/ 3000 MS          运行内存限制 : 65536 KByte
总提交 : 90            测试通过 : 23 

比赛描述

lqp刚学了辗转相除法,正不亦乐乎。will 又出来捣乱,给lqp 留了个难题。
 
给 N 个数,用 a1,a2…an来表示。现在will 让lqp依次取数,第一个数可以随意取。假使目前取得 aj,下一个数取ak(k>j),则ak必须满足gcd(aj,ak)≥L。 到底要取多少个数呢?自然是越多越好!

不用多说,这不仅是给lqp的难题,也是给你的难题。



输入

第一行包含两个数 L. 
接下来一行,有 N 个数用空格隔开,依次是 a1,a2…an.

输出

仅包含一行一个数,表示按上述取法,最多可以取的数的个数。

样例输入

5 6 
7 16 9 24 6 

样例输出

3

提示

选取 3个数16246gcd(16,24)=8gcd(24,6)=6。 
2≤L≤ai≤1 000 000

题目来源

JSOI2010




// AC 77MS
#include<iostream>
#define MAX_AI 1000001		//a[i]的最大值
#define MAX_N 50001			//数组A的最大长度,取1000的话会WA1
#define MAX_PRIME 1001		//处理的最大素数,可能会不够,不够的话再改吧
#define MAX_DIVISOR 1001	//约数最多个数
int N,L;
int a[MAX_N];
bool isPrime[MAX_PRIME];	//表示是否为素数
int primeNo;				//素数表的大小
int prime[MAX_PRIME];		//素数表
int primeFactorNo;			//某个a[i]的素因子表的大小
int primeFactor[MAX_N];		//某个a[i]的素因子表
int primeFactorN[MAX_N];	//某个a[i]每个素因子的幂
int divisorNo;				//某个a[i]的约数表的大小
int divisor[MAX_N];			//某个a[i]的约数表
char maxLen[MAX_AI];		//maxLen[k] 表示最后一个最大公约数为 k 的话,可以连成的最长队列是多少

void makePrimeTable(){
	int i,j;
	isPrime[2] = 1;
	for(i=3;i<MAX_PRIME;i+=2){
		isPrime[i] = 1;
	}
	for(i=3;i<MAX_PRIME;i+=2){
		if(isPrime[i]){
			for(j=(i<<1);j<MAX_PRIME;j+=i){
				isPrime[j] = 0;
			}
		}
	}
	for(primeNo=0,i=2;i<MAX_PRIME;i++){
		if(isPrime[i]){
			prime[++primeNo] = i;
		}
	}
}

// 将 k 拆分成素因子乘积的形式,存放于primeFactor[] 和primeFactorN[]中
void split(int k){
	int i;
	primeFactorNo = 0;
	for(i=1;i<primeNo && prime[i]<=k; i++){
		if(0==k%prime[i]){
			primeFactor[++primeFactorNo] = prime[i];
			primeFactorN[primeFactorNo] = 0;
			while(0==k%prime[i]){
				primeFactorN[primeFactorNo]++;
				k /= prime[i];
			}
		}
	}
	if(k!=1){
		primeFactor[++primeFactorNo] = k;				//最后一个存的可能不是素数
		primeFactorN[primeFactorNo] = 1;
	}
}

void dfs(int index, int Number){
	if(index>primeFactorNo){
		divisor[++divisorNo] = Number;
		return;
	}
	int i;
	dfs(index+1,Number);
	for(i=1;i<=primeFactorN[index];i++){
		Number *= primeFactor[index];
		dfs(index+1,Number);
	}
}

// 利用 primeFactor[]数组和primeFactorN[primeFactorNo] 制作约数表
void makeDivisorTable(){
	divisorNo = 0;
	dfs(1,1);
}

int refleshMaxLen(){
	int i,len=0;					//len记录用当前数字的约数作为最后一组公约数的最大长度
	for(i=1;i<=divisorNo;i++){
		if(divisor[i]>=L && maxLen[divisor[i]]>len){
			len = maxLen[divisor[i]];
		}
	}
	len++;							//有了当前分析的这个数字之后,长度增加一
	for(i=1;i<=divisorNo;i++){
		if(divisor[i]>=L){
			maxLen[divisor[i]] = len;
		}
	}
	return len;
}

int main(){
//	freopen("test.txt","r",stdin);
	int i,j,maxLength=0;
	makePrimeTable();
	scanf("%d%d",&N,&L);
	for(i=1;i<=N;i++){
		scanf("%d",a+i);
	}
	for(i=1;i<=N;i++){
		split(a[i]);
		makeDivisorTable();
		j = refleshMaxLen();
		if(maxLength<j){
			maxLength = j;
		}
	}
	printf("%d\n",maxLength);
}





你可能感兴趣的:(ACM,南邮OJ,取数游戏)