杜教筛学习笔记
\(\text{杜教筛学习笔记}\)
对于数论函数 \(f\),想求出 \(S(n)=\sum_{i=1}^nf(i)\) 采用线性筛的时间复杂度是 \(O(n)\)。采用杜教筛,可以将时间复杂度优化至亚线性时间复杂度。准确地说,是 \(O(n^{\frac 23})\)。
算法思想
对于数论函数 \(g\),有:
\[\begin{aligned}
\sum_{i=1}^n(f*g)(i)=&\sum_{i=1}^n\sum_{d\mid i}g(d)f(\frac id)\\
=&\sum_{i=1}^n\sum_{j=1}^{\lfloor\frac in\rfloor}g(i)f(j)\\
=&\sum_{i=1}^ng(i)\sum_{j=1}^{\lfloor\frac in\rfloor}f(j)\\
=&\sum_{i=1}^ng(i)S(\lfloor\frac ni\rfloor)
\end{aligned}
\]
那么会有:
\[\begin{aligned}
g(1)S(n)=&\sum_{i=1}^ng(i)S(\lfloor \frac ni\rfloor)-\sum_{i=2}^ng(i)S(\lfloor \frac ni\rfloor)\\
=&\sum_{i=1}^n(f*g)(i)-g(i)S(\lfloor \frac ni\rfloor)
\end{aligned}
\]
如果我们可以构造恰当的函数 \(g\) 使得 \(\sum_{i=1}^n(f*g)(i)\) 和 \(\sum_{i=2}^ng(i)\) 容易计算,那么 \(g(1)S(n)\) 就是容易求得的了。
需要留意的是,杜教筛的采用场景是 \(g\) 为积性函数,与 \(f\) 是否为积性函数没有关系。
对于杜教筛的时间复杂度,采取积分知识可证明为是 \(O(n^{\frac 34})\),如果我们采用线性筛预先筛出前 \(n^{\frac 23}\) 个数的预处理复杂度就降为了 \(O(n^{\frac 23})\)。
例题
- 求 \(S(n)=\sum_{i=1}^n\mu(i)\)。
 
由 \(\varepsilon=\mu*1\),于是构造 \(g(n)=\mathbf{1}(n)\),那么:
\[\begin{aligned}
S(n)=&\sum_{i=1}^n\mu(i)\\
=&\sum_{i=1}^n\varepsilon(i)-\sum_{i=2}^n\mathbf{1}(i)S(\lfloor \frac ni\rfloor)\\
=&1-\sum_{i=2}^nS(\lfloor \frac ni\rfloor)
\end{aligned}
\]
那么杜教筛求解即可。
实现上,需要留意的是杜教筛时间复杂度的保证来源于记忆化搜索,具体实现见代码。
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;
}

                
            
        
浙公网安备 33010602011771号