筛法(埃氏/欧拉/杜教/PN筛)

筛法

概述

一般的筛法可以筛出某个范围内的素数。

用恰当的筛法可以筛出具有某种性质的函数,或某种函数的前缀和。

埃氏筛

对于每一个素数 p,标记 kp+p2,k0 为合数。

正确性:假设 (p,p2) 内存在一个含有p这个因子的合数没被标记,那它一定可以被写为 pxqxzq 是质数的形式。

因为 pxq 小于 p2,所以 qp,所以这个数会被 q 标记,假设不成立。

所以可以每次遇到一个不是合数的数,就认为它是素数。

附上代码:

void ash_shai(int MAX){
	ll i = 2; for(; i * i <= MAX; ++i) if(!vis[i]){
		pri.push_back(i);
		for(ll j = i * i; j <= MAX; j += i) vis[j] = true;
	}
	for(; i <= MAX; ++i) if(!vis[i]) pri.push_back(i);
}

复杂度 O(nlog(logn)) 并且常数很小,所以可以近似看作 O(n)。OI-wiki上好像给出了一些神秘优化方法,不过我没看懂。

这个算法一般用于只需要筛素数的题。

欧式筛

又称线性筛。因为是严格 O(n) 的。

用这个算法主要是为了筛函数值。

对于合数,我们希望只用这个数的最小质因子把它筛掉。

我们对于每一个数i,枚举小于它并与其互质的质数 p,把由它俩乘积的这个合数打上标记。

正确性:我们认为这些合数是被 p 标记的。如果 p,i 不互质,那么该合数的最小质因子是 ip ,或 ip 的因子。如果 p 是合数,那么 ip 的最小质因子应该是 p 的因子。因为 p<i,且 i 没有小于 p 的因子,所以 p 就是 ip 的最小质因子。

每次遇到一个不是合数的数,就认为它是素数。

不过更重要的是用欧拉筛去筛函数值。

可以被欧拉筛筛出来的函数必须满足以下性质:

对于 $k = 1, k \in prime, k \mid p f(pk)$ 可以由 f(p)f(k) 快速得到,并且知道 f(1) 的值。

所以大部分的积性函数都可以用欧拉筛线性地筛出函数值。

例如 φμ

φ(1)=μ(1)=1

φ(p)=p1,μ(p)=1

φ(pk)=φ(p)φ(k),μ(pk)=μ(p)μ(k)k 是质数

φ(pk)=kφ(p),μ(pk)=0kp

φ(pk)=(k1)φ(p),μ(pk)=1μ(p)kprime

代码:

void get_oula(int M){
	phi[1] = 1, mu[1] = 1;
	for(int i=2;i<=M;++i){
		if(!vis[i]) pri.push_back(i), phi[i] = i - 1, mu[i] = -1;//k=1
		for(int j : pri) {//k is prime
			if(i*j>M) break;
			vis[i*j] = 1;
			phi[i*j] = phi[i] * (j - 1);
			mu[i*j] = mu[i] * (-1);
			if(i % j == 0){//k | p
				phi[i*j] = phi[i] * j;
				mu[i*j] = 0;
				break;
			}
		}
	}
	for(int i=1;i<=M;++i){
		sp[i] = sp[i - 1] + phi[i];
		sm[i] = sm[i - 1] + mu[i];
	}
}

image-20250208183053139

杜教筛

缺点:只能得到函数的部分函数值的前缀和。对要筛的函数有很多限制。

优点:1. 复杂度 O(n23).2. 可以得到前缀和。

正文

对于数论函数 f,计算 S(n)=i=1nf(i)

需要找到一个合适的函数 g,来和 f 进行卷积。

(1)in(fg)(i)=g(d)f(i/d)(2)=d=1ng(d)i=1n/df(i)(3)=d=1ng(d)S(n/d)(4)=g(1)S(n)+d=2ng(d)S(n/d)g(1)S(n)=in(fg)(i)d=2ng(d)S(n/d)

如果 g(1),in(fg)(i),d=2ng(d) 这三个都是能快速求到的,那么这似乎就可以用整除分块来递归地做。

最后一个问题:这个是什么时间复杂度?

观察到:当a,b>0b\Z 时, nab=nab,所以无论怎么递归,S 会计算到的数只在集合 {nx} 中,只有 O(n) 个。用微积分的知识,可以得到时间复杂度是 O(n34)

不过我们还可以通过暴力预处理(欧拉筛)前面的 23,使时间复杂度降到 O(n23).

最大的问题在与要有合适的 g。我们发现 常数函数 1φμ 就满足此性质。

μ带到我们的式子里:

1(1)S(n)=i=1nϵ(i)i=2n1(i)S(ni)

即:

S(n)=1i=2n1(i)S(ni)

1(i) 的前缀和就是 i。杜教筛时间复杂度 O(n23)

对于 φ 带到我们的式子里:

1(1)S(n)=i=1nid(i)i=2n1(i)S(ni)

即:

S(n)=n(n+1)2i=2n1(i)S(ni)

1(i) 的前缀和就是 i。杜教筛时间复杂度 O(n23)

我们还发现 μ·idkφ·idk 可以找到 idk 作为它们的函数 g。(注:这里的点乘表示的是对应位置相乘,即 :(f·g)(n)=f(n)g(n)

绪山真寻

Powerful Number筛​

这玩意相比杜教筛,优点是对 g 的限制更少,可以处理更积性的 f,缺点是写起来更史。

Powerful Number

定义:对于正整数 n,记 n 的质因数分解为 n=i=1mpiein 是 Powerful Number(简称 PN) 当且仅当 1im,ei>1

PN 有如下性质:

  • 所有 PN 都可以表示成 a2b3 的形式。
    证明:若 ei 是偶数,则将
    piei 合并进 a2 里;若 ei 为奇数,则先将 pi3 合并进 b3 里,再将 piei3 合并进 a2 里。

  • n 以内的 PN 有 O(n) 个。

证明:考虑枚举 a,再考虑满足条件的 b 的个数,有 PN 的个数约等于

1nnx23dx=O(n)

那么如何求出 n 以内所有的 PN 呢?线性筛找出 n 内的所有素数,再 DFS 搜索各素数的指数即可。由于 n 以内的 PN 至多有 O(n) 个,所以至多搜索 O(n) 次。

Powerful Number筛要求存在一个函数 g 满足:

  • g 是积性函数。

  • g 易求前缀和。

  • 对于质数 pg(p)=f(p)

假设现在要求积性函数 f 的前缀和.

首先,构造出一个易求前缀和的积性函数 g,且满足对于素数 pg(p)=f(p)。记 G(n)=i=1ng(i)

然后,设函数 h 满足 f=gh,根据狄利克雷卷积的性质可以得知 h 也为积性函数,因此 h(1)=1

对于素数 pf(p)=g(1)h(p)+g(p)h(1)=h(p)+g(p)h(p)=0。根据 h(p)=0h 是积性函数可以推出对于非 PN 的数 nh(n)=0,即 h 仅在 PN 处取有效值。这是保证 PN 筛时间复杂度的基本原理。

现在,根据 f=gh 有:

F(n)=i=1nf(i)=i=1nd|ih(d)g(id)=d=1ni=1ndh(d)g(i)=d=1nh(d)i=1ndg(i)=d=1nh(d)G(nd)=d=1d is PNnh(d)G(nd)

O(n) 找出所有 PN,计算出所有 h 的有效值。对于 h 有效值的计算,只需要计算出所有 h(pc) 处的值,就可以根据 h 为积性函数推出 h 的所有有效值。

现在对于每一个有效值 d,计算
h(d)G(nd) 并累加即可得到 F(n)

下面考虑计算 h(pc),一共有两种方法:

  • 直接推出 h(pc) 仅与 p,c 有关的计算公式,再根据公式计算 h(pc)
  • 根据 f=ghf(pc)=i=0cg(pi)h(pci),移项可得 h(pc)=f(pc)i=1cg(pi)h(pci),现在就可以枚举素数 p 再枚举指数 c 求解出所有 h(pc)

复杂度 O(nlogn),而且这个上界比较宽松。

可惜的是,我们没有 PN 筛的模板题。不过 Min_25 筛的模板题可以用 PN 筛来做。

Min_25筛

咕咕咕

洲阁筛

咕咕咕

posted @   花子の水晶植轮daisuki  阅读(27)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
https://blog-static.cnblogs.com/files/zouwangblog/mouse-click.js
点击右上角即可分享
微信分享提示