取数游戏
时间限制(普通/Java) :
1000 MS/ 3000 MS 运行内存限制 : 65536 KByte
总提交 : 90 测试通过 : 23
总提交 : 90 测试通过 : 23
比赛描述
lqp刚学了辗转相除法,正不亦乐乎。will 又出来捣乱,给lqp 留了个难题。
给 N 个数,用 a1,a2…an来表示。现在will 让lqp依次取数,第一个数可以随意取。假使目前取得 aj,下一个数取ak(k>j),则ak必须满足gcd(aj,ak)≥L。 到底要取多少个数呢?自然是越多越好!
不用多说,这不仅是给lqp的难题,也是给你的难题。
输入
第一行包含两个数N 和 L.
接下来一行,有 N 个数用空格隔开,依次是 a1,a2…an.
输出
仅包含一行一个数,表示按上述取法,最多可以取的数的个数。
样例输入
5 6
7 16 9 24 6
样例输出
3
提示
选取 3个数16、24、6。gcd(16,24)=8,gcd(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);
}