题目描述#
根据质因子唯一分解定理可知n=pk11pk22…pkmm,其中pi都是质数。我们定义f(n)=m, 求g(a,b)=∑bi=af(i)。
输入#
第一行是一个整数T(1≤T≤1000),表示样例的个数。
以后每个样例占一行,为两个整数 a(2≤a≤b≤106)。
输出#
依次每行输出一个样例的结果,为一个整数。
样例输入#
2 2 2 2 10样例输出#
1 11
解题思路:这题还是质因数分解的题,另外 前缀和 肯定也是跑不了的。
但是这次换个不一样的解法,我们利用 欧拉筛 的筛法性质,来解这道题, 关键代码就17、19两行,如果能理解到,也是说明你对欧筛的理解完全ok了。
首先知道,欧筛是一个线性筛,所有数只需要遍历一遍,就能判断出它是否为质数,这里,我们还是把质数标记为1(第13行),因为它本身质因数分解就它自己,所以就是 f(n) = 1。
然后17行,令 vis[i*prime[j]] = vis[i],即 f(i的倍数) = f(i),因为 i*prime[j] = i * prime[j],我们默认这个 prime[j] 已经是 数字 i 的质因数,所以定义 f(i*n) = f(i)。
那什么时候执行19行,vis++操作? 当i % prime[j] != 0 ,即 prime[j] 是一个新的质因数时,vis++, (例如:i = 3 时,vis[3*2] = vis[3] = 1, 如果没有break掉,说明 i 以前没有2这个质因数,而这里 又有 i = 3*2,所以 vis[3*2] 要 ++(加上2这个因数))
以此类推,vis数组最后就 既是 质数的标记数组,又是 前缀和的记录数组 (vis的值,就是 f(i) 的值)
AC代码:
#include <stdio.h>
const int N = 1e6;
int vis[N+2] = {0};
int prime[80000] = {0};
void is_prime()
{
for (int i = 2; i < N; i ++) // 欧筛
{
if (!vis[i])
{
prime[++prime[0]] = i;
vis[i] = 1;
}
for (int j = 1; j <= prime[0] && i <= N/prime[j]; j ++)
{
vis[i*prime[j]] = vis[i];
if (i % prime[j] == 0) break;
vis[i*prime[j]] += 1; // 如果没break掉,说明是prime[j]对i来说是一个新的质因数,同理是 i*prime[j]的新质因数
}
}
for (int i = 2; i <= N; i ++) // 前缀和
vis[i] += vis[i-1];
}
int main()
{
is_prime();
int T;
scanf("%d",&T);
while ( T --)
{
int a,b;
scanf("%d %d",&a,&b);
printf("%d\n",vis[b]-vis[a-1]);
}
return 0;
}