【题解】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\) 进行离散化。

  1. 二分部分:找到第一个比 \(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;
}
  1. 筛法部分:筛并且预处理
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;
	}
}  
  1. 分解质因数与预处理
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]; //前缀和
  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; //输出
posted @ 2023-11-06 14:00  邻补角-SSA  阅读(8)  评论(0)    收藏  举报  来源