题目描述:
一个除1以外的正整数都能被分解为若干个质因子的乘积。比如24 = 2 * 2 * 2 * 3。现在给你m, n, 求[m, n]内的所有正整数被分解为质因子的个数总和。
例如 : m = 5, n = 9时 :
5 = 5 --- 1
6 = 2 * 3 --- 2
7 = 7 --- 1
8 = 2 * 2 * 2 --- 3
9 = 3 * 3 --- 2
所以[5, 9]内的所有正整数被分解为质因子的个数总和为1+2+1+3+2=9个。
输入描述:
输入为多组数据。第一行为一个整数t(1<=t<=100000), 代表数据的组数。紧接着为t行,每行有2个数m, n(2<=m<=n<=10000000), m, n之间间隔一个空格。
输出描述:
对于每个数据,输出一行,这行仅含一个整数,为[m, n]内的所有正整数被分解为质因子的个数总和。
输入样例:
2
5 9
11 20
输出样例:
9
21
题目分析:
首先分析题目,所谓的质因数,就是既是质数,也就是素数,也是因子,那么,我们首先要求的就是素数。由于数据的范围是2到一千万,那么,我们首先就可以开一个一千万零一大小的数组,运用素数筛选法来标记处2到一千万得数哪些是素数,哪些不是。很好,第一个问题解决了。接着,我们分析质因数,首先看样例输入,5到9,5的质因数为1个,7的质因数也为一个,很明显,一个素数的质因数只有一个,那么我们来分析合数,8可以分成2*2*2,那么8的质因数就为3个,同时,2*2*2也就可以等价于2*4,我们来看看,2是素数,所以2的质因数为1个,4为合数,可以分解成2*2,也就是说,4有2个质因数,1+2=3,是不是很熟对于悉?我们似乎发现了一个规律,num[i] = num[j] + num[m],(j * m == i),那么这个递推式正不正确呢?我们再看,9=3*3,也就是两个质因数的乘积,而3为素数,有1个质因数,那么,2 = 1 + 1,这似乎就印证了这个递推式,其实不难理解,因为每一个数字都可以分解为若干质因数的乘积,那么,对于每一个j * m == i,j和m也都可以分解为若干质因数的乘积,那么i的质因数乘积个数也就等于j的质因数乘积个数+m的质因数乘积个数。或许到这里你就以为求出每一个数字的质因数乘积所含质因数个数,再在m-n范围内求和就行了,No!我们还有一个问题没有考虑,由于每一次都有多组测试数据,那么,就很容易出现这种情况:每一次的n和m都有大量重复范围,在这种情况下,由于组数的大,就会导致时间大大增加,最后就很有可能TLE。
那么,我们怎么解决这个大量数据重复的问题呢?我们先看这样一个例子:第一次我们要求1到100的整数个数,那么其实就是1到100的整数个数减去1到1的整数个数,有没有发现解决办法呢?相信聪明的你已经发现了,那就是:
对于每一个数字m,我们都求出2到m的质因数乘积的和,这样我们就会用了O(n)的复杂度求出每一个数字2到该数字的质因数乘积所含质因数之和,最后对于每一组数据n和m,我们只需要输出num[n] - num[m - 1]就可以了。下面给出AC代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define Maxn 10000001
int prime[1000000];
int a[Maxn];
int main(void)
{
int i,j,t,m,n,sum1,k,sum2,m1,n1,num = -1;
scanf ("%d",&t);
k = t;
for (i = 2; i < Maxn; i++)
{
a[i] = 1;
}
//线性筛选法求素数
for (i = 2; i < Maxn; i++)
{
//如果一个数是素数,则将其加入prime数组
if(a[i])
{
prime[++num] = i;
}
//开始筛选素因子,并且只从前面已经筛选的素数开始筛选素因子
for (j = 0; j <= num && i * prime[j] < Maxn; j++)
{
//一个素数的倍数必然不是素数
a[i * prime[j]] = 0;
//如果i能被prime[j]整除,则prime[j + 1]必然在之后能被处理,这里是关键,大大减少时间复杂度
if(i % prime[j] == 0)
{
break;
}
}
}
for (i = 2; i < Maxn; i++)
{
if(!a[i])
{
for (j = 2; j < i; j++)
{
m = i / j;
if(m * j == i)
{
a[i] = a[m] + a[j];
break;
}
}
}
}
for (i = 3; i < Maxn; i++)
{
a[i] += a[i - 1];
}
while (t--)
{
scanf ("%d%d",&m,&n);
sum1 = a[n] - a[m - 1];
printf ("%d\n",sum1);
}
return 0;
}