关于杜教筛

杜教筛

  这个东西的前置芝士有点多啊~~

  这个东西差不多就是拿来求函数前缀和的一个东西,他能做到在 O ( n 2 3 ) O(n^{\frac 23}) O(n32) 的时间复杂度内求出答案:

S ( n ) = ∑ i = 1 n f ( i ) S(n)= \sum_{i = 1}^nf(i) S(n)=i=1nf(i)

欧拉函数前缀和

  我们先通过求欧拉函数的前缀和来感受一下杜教筛的思想,记答案为:

S ( n ) = ∑ i = 1 n φ ( n ) S(n) = \sum_{i = 1}^n \varphi(n) S(n)=i=1nφ(n)

  然后根据众所周知的一个公式 ( φ ∗ I ) ( n ) = i d ( n ) (\varphi * I)(n) = id(n) (φI)(n)=id(n),我们可以进行一下的推导:

n = φ ∗ I = ∑ d ∣ n φ ( d ) = φ ( n ) + ∑ d ∣ n ∧ d < n φ ( d ) φ ( n ) = n − ∑ d ∣ n ∧ d < n φ ( d ) S ( n ) = ∑ i = 1 n φ ( i ) = ∑ i = 1 n ( i − ∑ d ∣ i ∧ d < i φ ( d ) ) = ∑ i = 1 n i − ∑ i = 1 n ∑ d ∣ i ∧ d < i φ ( d ) = 1 2 n ( n − 1 ) − ∑ ⌊ i d ⌋ = 2 n ∑ d = 1 ⌊ n i d ⌋ φ ( d ) = 1 2 n ( n − 1 ) − ∑ i = 2 n ∑ d = 1 ⌊ n i ⌋ φ ( d ) = 1 2 n ( n − 1 ) − ∑ i = 2 n S ( ⌊ n i ⌋ ) \begin{aligned} n = \varphi * I & = \sum_{d | n}\varphi(d) = \varphi(n) + \sum_{d | n \land d < n}\varphi(d) \\ \varphi(n) & = n - \sum_{d |n \land d < n}\varphi(d)\\ S(n) = \sum_{i = 1}^n\varphi(i) & = \sum_{i = 1}^n\left(i - \sum_{d | i \land d < i}\varphi(d)\right) = \sum_{i = 1}^ni - \sum_{i = 1}^n\sum_{d | i \land d < i}\varphi(d) \\ & = \frac12n(n-1) - \sum_{\lfloor \frac id \rfloor = 2}^n\sum_{d =1}^{\left\lfloor \frac{n}{\frac id} \right\rfloor}\varphi(d) = \frac 12 n (n - 1) - \sum_{i = 2}^n\sum_{d=1}^{\lfloor \frac ni \rfloor}\varphi(d) \\ & = \frac 12 n(n - 1) - \sum_{i=2}^nS \left(\left\lfloor\frac ni \right\rfloor\right) \end{aligned} n=φIφ(n)S(n)=i=1nφ(i)=dnφ(d)=φ(n)+dnd<nφ(d)=ndnd<nφ(d)=i=1nidid<iφ(d)=i=1nii=1ndid<iφ(d)=21n(n1)di=2nd=1dinφ(d)=21n(n1)i=2nd=1inφ(d)=21n(n1)i=2nS(in)

  这样我们就得到了:

S ( n ) = 1 2 n ( n − 1 ) − ∑ i = 2 n S ( ⌊ n i ⌋ ) S(n) = \frac 12 n (n - 1) - \sum_{i =2}^nS \left(\left\lfloor \frac ni \right\rfloor\right) S(n)=21n(n1)i=2nS(in)

  我们发现这个式子就是一个 S ( n ) S(n) S(n) 的递推式,而且 1 2 n ( n − 1 ) \frac 12 n (n - 1) 21n(n1) 这一项的时间复杂度是 O ( 1 ) O(1) O(1) 的,所以我们只用要计算复杂度的时候可以把这一项略去。所以我们只需要计算这个式子的时间复杂度:

S ( n ) = ∑ i = 2 n S ( ⌊ n i ⌋ ) S(n) = \sum_{i = 2}^nS(\left\lfloor \frac ni \right\rfloor) S(n)=i=2nS(in)

  然后我们发现右边可以整出分块,就是有 n \sqrt{n} n S ( n i ) S(\frac ni) S(in) 的取值,最后加起来还有 O ( n ) O(\sqrt{n}) O(n ),所以,我们设 S ( n ) S(n) S(n) 的时间复杂度是 T ( n ) T(n) T(n),那我们就有:

T ( n ) = O ( n ) + O ( ∑ i = 2 n T ( n i ) ) T(n) = O(\sqrt{n}) + O(\sum_{i = 2}^{\sqrt{n}}T(\frac ni)) T(n)=O(n )+O(i=2n T(in))

  再展开一层(中间的第二项太小了直接略去):

T ( n i ) = O ( n i ) + O ( ∑ k = 2 n i T ( n i k ) ) = O ( n i ) T\left(\frac ni\right) = O(\sqrt{\frac ni}) + O(\sum_{k = 2}^{\sqrt{\frac ni}}T(\frac{\frac ni}{k})) = O(\sqrt{\frac ni}) T(in)=O(in )+O(k=2in T(kin))=O(in )

  所以我们将这个式子带回:

T ( n ) = O ( n + O ( ∑ i = 2 n n i ) = O ( ∑ i = 2 n n i ) ≈ O ( ∫ 0 n n x d x ) = O ( n 3 4 ) T(n) = O(\sqrt{n} + O(\sum_{i = 2}^{\sqrt{n}}\sqrt{\frac ni}) = O(\sum_{i = 2}^{\sqrt{n}}\sqrt{\frac ni}) \approx O(\int_0^{\sqrt{n}}\sqrt{\frac nx}dx) = O(n^{\frac 34}) T(n)=O(n +O(i=2n in )=O(i=2n in )O(0n xn dx)=O(n43)

  但是直接递归的做法不是最优的,我们还可以考虑先用线性筛筛出前 k k k S ( n ) S(n) S(n),这样显然可以降低一些复杂度。我们考虑筛出前 k k k S ( n ) S(n) S(n) k ≥ n k \geq \sqrt{n} kn ,所以我们现在只用求的是 k < ⌊ n i ⌋ ≤ n k < \lfloor \frac ni \rfloor \leq n k<inn 的部分就能解决问题了,也就是计算 1 < i ≤ n k 1 < i \leq \frac nk 1<ikn 的部分。那么时间复杂度就是:

T ( n ) = ∑ i = 2 n k O ( n i ) ≈ O ( ∫ 0 n k n k d x ) = O ( n k ) T(n) = \sum_{i = 2}^{\frac nk}O(\sqrt{\frac ni}) \approx O(\int_0^{\frac nk} \sqrt{\frac nk} dx) = O(\frac{n}{\sqrt{k}}) T(n)=i=2knO(in )O(0knkn dx)=O(k n)

  因为线性筛也要花时间,也就是 O ( k ) O(k) O(k) 的时间,所以总的复杂度应该为:

T ( n ) = O ( k ) + O ( n k ) T(n) = O(k) + O(\frac{n}{\sqrt{k}}) T(n)=O(k)+O(k n)

  我们通过一些计算手段可以得到 k k k 差不多取到 O ( n 2 3 ) O(n^{\frac 23}) O(n32) 的时候 T ( n ) T(n) T(n) 是最小的,复杂度就是:

O ( n 2 3 + O ( n n 2 3 ) ) = O ( n 2 3 ) O(n^{\frac 23} +O(\frac {n}{\sqrt{n^{\frac 23}}})) = O(n^{\frac 23}) O(n32+O(n32 n))=O(n32)

莫比乌斯函数

  推导公式和计算的过程基本和欧拉函数都是差不多的在这里给出计算过程,不做详细解释:

( μ ∗ I ) ( n ) = ε ( n ) = [ n = 1 ] [ n = 1 ] = ∑ d ∣ n μ ( d ) = μ ( n ) + ∑ d ∣ n ∧ d < n μ ( d ) μ ( n ) = [ n = 1 ] − ∑ d ∣ n ∧ d < n μ ( d ) S ( n ) = ∑ i = 1 n μ ( i ) = ∑ i = 1 n ( [ i = 1 ] − ∑ d ∣ i ∧ d < i μ ( d ) ) = 1 − ∑ i = 1 n ∑ d ∣ i ∧ d < i μ ( d ) = 1 − ∑ ⌊ i d ⌋ = 2 n ∑ d = 1 ⌊ n i d ⌋ μ ( d ) = 1 − ∑ i = 2 n ∑ d = 1 ⌊ n i ⌋ μ ( d ) = 1 − ∑ i = 2 n S ( ⌊ n i ⌋ ) S ( n ) = 1 − ∑ i = 2 n S ( ⌊ n i ⌋ ) \begin{aligned} (\mu * I)&(n) = \varepsilon(n) = [n = 1] \\ [n = 1] & = \sum_{d | n}\mu(d) = \mu(n) + \sum_{d |n \land d < n}\mu(d) \\ \mu(n) & = [n = 1] - \sum_{d | n \land d < n}\mu(d) \\ S(n) = \sum_{i = 1}^n \mu(i) & = \sum_{i = 1}^n\left( [i = 1] - \sum_{d | i \land d < i}\mu(d) \right) = 1 - \sum_{i = 1}^n \sum_{d | i \land d < i}\mu(d) \\ & = 1 - \sum_{\lfloor\frac id\rfloor = 2}^n\sum_{d = 1}^{\lfloor \frac{n}{\frac id} \rfloor}\mu(d) = 1 - \sum_{i = 2}^n\sum_{d = 1}^{\lfloor \frac ni \rfloor}\mu(d) = 1 - \sum_{i = 2}^n S\left(\left\lfloor \frac ni \right\rfloor\right) \\ S(n) &= 1 - \sum_{i = 2}^nS\left(\left\lfloor \frac ni \right\rfloor\right) \end{aligned} (μI)[n=1]μ(n)S(n)=i=1nμ(i)S(n)(n)=ε(n)=[n=1]=dnμ(d)=μ(n)+dnd<nμ(d)=[n=1]dnd<nμ(d)=i=1n[i=1]did<iμ(d)=1i=1ndid<iμ(d)=1di=2nd=1dinμ(d)=1i=2nd=1inμ(d)=1i=2nS(in)=1i=2nS(in)

代码

  传送门:luogu4213 杜教筛

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100001000
#define ll long long

int T = 0; int n = 0;
bool vi[MAXN] = { 0 };
int pri[MAXN] = { 0 }; int m = 0;
ll   ph[MAXN] = { 0 };
int  mu[MAXN] = { 0 };
unordered_map<int, ll> P, M;

void Sieve(int n){
	mu[1] = ph[1] = 1;
	for(int i = 2; i <= n; i++){
		if(!vi[i]) pri[++m] = i, ph[i] = i - 1, mu[i] = -1;
		for(int j = 1; j <= m; j++){
			if(i * pri[j] > n) break;
			vi[i * pri[j]] = 1;
			if(i % pri[j] == 0){
				ph[i * pri[j]] = ph[i] * pri[j];
				mu[i * pri[j]] = 0; break;
			}
			mu[i * pri[j]] = -mu[i];
			ph[i * pri[j]] = ph[i] * (pri[j] - 1);
		}
	}
	for(int i = 2; i <= n; i++)
		mu[i] += mu[i - 1], ph[i] += ph[i - 1];
}

ll Phi(ll n){
	if(n <= 1e7) return ph[n];
	if(P.count(n)) return P[n];
	ll x = 1ll * n * (n + 1) / 2;
	for(ll l = 2, r; l <= n; l = r + 1){
		int v = n / l; r = n / v;
		x -= 1ll * (r - l + 1) * Phi(v);
	}
	return P[n] = x;
}

ll Mu(ll n){
	if(n <= 1e7) return mu[n];
	if(M.count(n)) return M[n];
	ll x = 1;
	for(ll l = 2, r; l <= n; l = r + 1){
		int v = n / l; r = n / v;
		x -= 1ll * (r - l + 1) * Mu(v);
	}
	return M[n] = x;
} 

int main(){
	cin >> T;
	Sieve(1e7);
	while(T--){
		cin >> n;
		M.clear(); P.clear();
		cout << Phi(n) << ' ' << Mu(n) << '\n';
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值