【题解】CF385C 题解
CF385C 题解
一道比较简单的数论题。
思路也比较明显:预处理出范围内所有 \(f(p)\) 的值,然后对于每次询问,计算 \(l\) 到 \(r\) 中质数 \(p\) 的 \(f(p)\) 值之和即可。
发现要求区间求和,而且不要求修改,考虑使用前缀和优化之。
现在问题就是怎么预处理出范围内所有 \(f(p)\) 的值,
直接每个质数暴力求显然会超时,因此反过来做:枚举序列中每个数,对其分解质因数,对于每个不同的质因数 \(p\),\(f(p)\) 加一即可。
于是现在难点就在怎么分解出不同的质因数,传统的 \(O(\sqrt{m})\) 的分解方法不可行。这里提供一种复杂度近似于 \(O(\log_2 m)\) 的方法:
记待分解的数为 \(t\),先预处理每个数的最小质因数 \(u_i\),然后循环执行如下步骤直到 \(u_t\) 为 \(0\):
-
\(r=u_t\),则 \(r\) 为本次分解出的质因数。
-
当 \(r \mid t\) 时,将 \(t\) 除以 \(r\)。
而对于 \(u\) 的预处理,欧拉筛,埃氏筛均可。
然后做完!
关键代码
这里有个细节:要对 \(p\) 进行离散化。
- 二分部分:找到第一个比 \(s\) 小的质数的下标
int lw_b(int s) //找到第一个比 s 小的质数的下标
{
int l = 1, r = u;
while(l <= r)
{
int mid = (l + r) >> 1;
if(p[mid] <= s) l = mid + 1;
else r = mid - 1;
}
return r;
}
- 筛法部分:筛并且预处理
memset(scz, true, sizeof(scz)); //初始化质数判断数组
scz[1] = 0; //1 不是质数
for(int i = 2;i <= 10000000;i++) //欧拉筛
{
if(scz[i])
{
p[++u] = i;
f[i] = u; //记录最小质因数
}
for(int j = 1;j <= u && i * p[j] <= 10000000;j++)
{
scz[i * p[j]] = 0;
f[i * p[j]] = j; //记录最小质因数
if(i % p[j] == 0) break;
}
}
- 分解质因数与预处理
for(int i = 1;i <= n;i++)
{
int t;
cin >> t;
while(f[t]) //分解质因数
{
c[f[t]]++;
int v = f[t];
while(t % p[v] == 0) t /= p[v];
}
}
for(int i = 1;i <= u;i++) c[i] += c[i - 1]; //前缀和
- 询问处理部分
int l, r;
cin >> l >> r;
int g = lower_bound(p + 1, p + u + 1, l) - p; //计算质数下标
int v = lw_b(r);
cout << c[v] - c[g - 1] << endl; //输出

浙公网安备 33010602011771号