2017网络新生赛2308The Sum of Prime Factor心得

题目描述:

一个除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;
}

 

转载于:https://www.cnblogs.com/Tang123456/p/7862438.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值