简单数论 线性筛

Diff-prime Pairs
我们可以发现任意两个的素数都符合题意
但是像 4 , 6 4,6 4,6 这样的合数也符合条件,它本质上就是 2 ∗ 2 , 2 ∗ 3 2*2,2*3 22,23,也就是 2 , 3 2,3 2,3 这对素数的拓展。
那么对于任意一对素数可以拓展多少对呢,我们可以发现他们的 g c d gcd gcd 是相同的,因此决定一对素数贡献的是其中较大的一个素数。
2 ∗ x < = n , 3 ∗ x < = n 2*x<=n,3*x<=n 2x<=n,3x<=n
3 3 3 与比它小的素数 2 2 2 产生的贡献就是 n 3 ( 因 子 含 有 3 的 数 的 个 数 \frac{n}{3}(因子含有3的数的个数 3n3
因此我们记录一下比素数 i i i 小的素数的个数, i i i 与前 c n t cnt cnt 个素数产生的贡献就是 n i ∗ c n t \frac{n}{i} * cnt incnt
code:

#include<bits/stdc++.h>
#define ll long long

using namespace std;
const int maxn = 1e7 + 9;
int p[maxn], v[maxn], cnt;
ll n;
void init()
{
	for(int i = 2; i <= maxn - 9; ++i)
	{
		if(!v[i]) p[++cnt] = i;
		for(int j = 1; j <= cnt && 1ll * i * p[j] <= maxn - 9; ++j)
		{
			v[i * p[j]] = 1;
			if(i % p[j] == 0){
				break;
			}
		}
	}
}

void work()
{
	cin >> n;
	ll ans = 0, cnt = 0;
	for(int i = 2; i <= n; ++i) if(!v[i]){
		ans += (n / i) * cnt;
		++cnt;
	}
	cout << ans * 2 << endl;
}

int main()
{
	ios::sync_with_stdio(0);
	init();
	work();
	return 0;
}

<think> 首先,用户的问题是:“我想查询线性筛的时间复杂度是否为O(n) 请问线性筛的时间复杂度是否为O(n)”。用户想确认线性筛算法的时间复杂度是否为 O(n)。 从系统级指令中,我有几个关键点: - 所有行内数学表达式必须使用 $...$ 格式。 - 独立公式必须使用 $$...$$ 格式并单独成段。 - 使用 LaTeX 语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如 [^1]。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 用户提供了几个引用: - 引用[1]:关于线性筛求因子个数。 - 引用[2]:短小精悍的线性时间素数筛法。 - 引用[3]:线性筛法 O(n)。 - 引用[4]:素数筛的改进-线性筛o(n)时间复杂度。 这些引用都提到了线性筛,并暗示时间复杂度是 O(n)。引用[2]的标题是“线性时间素数筛法”,引用[3]说“线性筛法 O(n)”,引用[4]说“线性筛o(n)时间复杂度”。所以,这些支持了线性筛的时间复杂度是 O(n)。 现在,我需要基于这些信息回答问题。 步骤: 1. **确认时间复杂度**:线性筛(也称为欧拉筛)的时间复杂度是 O(n)。这是因为每个合数只被其最小质因数筛除一次,因此每个数只被访问一次。 2. **结构化回答**: - 开头直接回答问题:是的,线性筛的时间复杂度是 O(n)。 - 然后解释为什么是 O(n),使用清晰的步骤。 - 引用相关部分来支持。 - 使用 LaTeX 格式数学表达式。 - 最后生成相关问题。 3. **使用 LaTeX**: - 行内:例如,$O(n)$。 - 独立公式:如果需要,比如筛法的原理。 4. **生成相关问题**:在回答末尾,添加几个相关问题,格式如示例中的“§§相关问题§§”。 5. **引用**:在解释中自然地添加引用。例如,在提到线性筛的实现时,引用[2]或[3]。 详细回答: - **肯定回答**:是的,线性筛的时间复杂度是 $O(n)$。 - **解释**: - 线性筛通过每个合数仅被其最小质因数筛除一次来实现线性时间复杂度。 - 算法遍历每个数 i 从 2 到 n。 - 对于每个数,如果是质数,则添加到质数列表中。 - 然后,对于每个质数 p,如果 p 整除 i,则标记 i * p 为合数,并 break(确保每个合数只被标记一次)。 - 代码示例:可以简要展示,但用户提供了代码,所以我可以引用。 - 时间复杂度分析:每个数只被处理一次,总操作次数是 O(n)。 引用: - 在提到时间复杂度时,引用[2]和[3],因为他们明确说 O(n)。 - 例如,“如引用[2]和[3]所示,线性筛的时间复杂度为 $O(n)$。” 相关问题: 基于主题,生成几个相关问题: 1. 线性筛与埃拉托斯特尼筛的区别? 2. 如何证明线性筛的时间复杂度是 O(n)? 3. 线性筛在实际应用中的优势? 4. 线性筛可以用于其他问题吗?如求因子个数(引用[1])。 确保回答真实可靠:线性筛的时间复杂度确实是 O(n),这是标准知识。 </think> 是的,线性筛(Euler 筛法)的时间复杂度为 $O(n)$。这意味着算法在处理大小为 $n$ 的输入时,其运行时间与 $n$ 成线性关系,是一种高效的素数筛选算法。下面我将逐步解释为什么时间复杂度是 $O(n)$,并提供相关细节以确保回答清晰可靠。 ### 1. **线性筛的核心原理** 线性筛通过确保每个合数仅被其最小质因数筛除一次来实现高效性。算法使用一个质数列表(存储已发现的质数)和一个标记数组(标记合数)。具体步骤如下: - 遍历每个整数 $i$ 从 2 到 $n$。 - 如果 $i$ 未被标记为合数(即它是质数),则将其加入质数列表。 - 对于每个质数 $p$(当前质数列表中的元素),标记 $i \times p$ 为合数: - 如果 $p$ 整除 $i$(即 $i \mod p = 0$),则终止内层循环(这确保每个合数只被最小质因数筛除一次)。 算法的关键在于:每个合数 $m$ 只被其最小质因数 $p$ 筛除(即当 $i = m / p$ 时),因此每个数只被处理一次,总操作次数为 $O(n)$。 ### 2. **时间复杂度分析** - **操作次数**:每个数 $i$(从 2 到 $n$)只被访问一次: - 如果 $i$ 是质数,添加到列表中。 - 对于每个 $i$,内层循环遍历质数列表,但每个质数 $p$ 筛除 $i \times p$ 的操作只有常数时间。 - **关键点**:由于内层循环在 $i \mod p = 0$ 时终止,避免了重复筛除。例如: - 当 $i = 4$ 时,质数 $p = 2$ 筛除 $4 \times 2 = 8$,然后终止(因为 $4 \mod 2 = 0$),确保 8 不会被其他质数重复筛除。 - **数学证明**:设 $T(n)$ 为总操作次数。每个合数 $m$ 仅被筛除一次,因此操作次数为 $\sum_{i=2}^{n} O(1) = O(n)$。引用[2]和[3]明确描述了这种线性时间行为[^2][^3]。 简单代码示例(基于引用[3]和[4])展示了 $O(n)$ 的实现: ```cpp #include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; bool st[N]; // 标记数组,false 表示质数 int p[N], cnt; // p[] 存储质数列表,cnt 计数 void get_primes(int n) { for (int i = 2; i <= n; i++) { if (!st[i]) p[cnt++] = i; // i 是质数,加入列表 for (int j = 0; p[j] * i <= n; j++) { st[p[j] * i] = true; // 标记合数 if (i % p[j] == 0) break; // 关键:确保只被最小质因数筛除 } } } ``` - 时间复杂度:外层循环 $O(n)$,内层循环总次数为 $O(n)$(因为每个合数只被处理一次),整体 $O(n)$[^3][^4]。 ### 3. **与其他筛法的比较** - **埃拉托斯特尼筛法**:时间复杂度 $O(n \log \log n)$,因为它会重复筛除某些合数(例如,12 被 2 和 3 重复筛除)。 - **线性筛的优势**:通过最小质因数优化,避免了重复操作,实现真正的线性时间 $O(n)$。引用[4]强调了这种改进的效率和适用性[^4]。 ### 4. **实际应用与注意事项** - **适用场景**:线性筛常用于需要快速生成质数列表的问题,如密码学、数论计算(如引用[1]中求因子个数的扩展)[^1]。 - **空间复杂度**:$O(n)$(用于存储标记数组和质数列表)。 - **可靠性**:该时间复杂度是理论证明的,并被广泛用于算法竞赛和库实现(如 C++ 标准库的优化版本)。 总之,线性筛的时间复杂度确实是 $O(n)$,这得益于其避免重复筛除的设计。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值