杜教筛学习笔记

杜教筛学习笔记

对于数论函数 f,想求出 S(n)=i=1nf(i) 采用线性筛的时间复杂度是 O(n)。采用杜教筛,可以将时间复杂度优化至亚线性时间复杂度。准确地说,是 O(n23)

算法思想

对于数论函数 g,有:

i=1n(fg)(i)=i=1ndig(d)f(id)=i=1nj=1ing(i)f(j)=i=1ng(i)j=1inf(j)=i=1ng(i)S(ni)

那么会有:

g(1)S(n)=i=1ng(i)S(ni)i=2ng(i)S(ni)=i=1n(fg)(i)g(i)S(ni)

如果我们可以构造恰当的函数 g 使得 i=1n(fg)(i)i=2ng(i) 容易计算,那么 g(1)S(n) 就是容易求得的了。

需要留意的是,杜教筛的采用场景是 g 为积性函数,与 f 是否为积性函数没有关系。

对于杜教筛的时间复杂度,采取积分知识可证明为是 O(n34),如果我们采用线性筛预先筛出前 n23 个数的预处理复杂度就降为了 O(n23)

例题

  • S(n)=i=1nμ(i)

ε=μ1,于是构造 g(n)=1(n),那么:

S(n)=i=1nμ(i)=i=1nε(i)i=2n1(i)S(ni)=1i=2nS(ni)

那么杜教筛求解即可。

实现上,需要留意的是杜教筛时间复杂度的保证来源于记忆化搜索,具体实现见代码。

vector<int>prm;
bool is[M];
int mu[M];
void solve() {
	mu[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!is[i]) {
			prm.push_back(i);
			mu[i] = -1;
		}
		for (auto j : prm) {
			if (i * j > N) break;
			is[i * j] = 1;
			if (i % j == 0) {
				mu[i * j] = 0;
				break;
			} 
			mu[i * j] = -mu[i];
		}
	}
	for (int i = 1; i <= N; i++) mu[i] += mu[i - 1];
}
unordered_map<int, int>mp;
int smu(int n) {
	if (n <= N) return mu[n];
	if (mp.count(n)) return mp[n];
	int l = 2, r = 0, ans = 1;
	while (l <= n) {
		r = n / (n / l);
		ans -= smu(n / l) * (r - l + 1);
		l = r + 1;
	}
	return mp[n] = ans;
}
posted @   长安19路  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示