杜教筛

有一数论函数 \(f\),求,

\[S(n) = \sum\limits_{i=1}^{n}f(i) \]

构造两个积性函数 \(h\)\(g\) 满足 \(h = f * g\)

\[\sum\limits_{i=1}^n f*g \]

有,

\[\begin{align*} \sum\limits_{i=1}^n f*g & = \sum\limits_{i=1}^n\sum\limits_{d|i}g(d)f(\frac{i}{d}) \\ & = \sum\limits_{d=1}^n g(d) \sum\limits_{i=1}^{\frac{n}{d}}f(i) \\ & = g(1)S(n) + \sum\limits_{d=2}^n g(d)S(\lfloor\frac{n}{d}\rfloor) \end{align*} \]

接着,我们需要预处理 \(S(n) \leq 10^6\) 的前缀和,以方便计算。

在外部,使用记忆化搜索及整除分块,以保证时间复杂度是 \(O(n^\frac{2}{3})\)

在内部,使用递归以快速求解。

以计算 \(\mu\)\(\varphi\) 为例,我们有如下代码:

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

const int N = 1e6+10;

int phi[N], mu[N], suphi[N], sumu[N], ans1, ans2;
bool notPrime[N];
vector<int>prime;
map<int, int>sudphi, sudmu;

int djsmu(int n){
	if(sudmu.count(n)) return sudmu[n];
	if(n <= 1e6) return sumu[n];
	int ans = 1;
	for(int l=2, r=0;l<=n;l=r+1){
		r = (n/(n/l));
		ans -= djsmu(n/l) * (r-l+1);
	}
	return sudmu[n] = ans;
}

int djsphi(int n){
	if(sudphi.count(n)) return sudphi[n];
	if(n <= 1e6) return suphi[n];
	int ans = (n*(n+1))/2;
	for(int l=2, r=0;l<=n;l=r+1){
		r = (n/(n/l));
		ans -= djsphi(n/l) * (r-l+1);
	}
	return sudphi[n] = ans;
}

void euler(int n){
	phi[1] = 1, mu[1] = 1;
	for(int i=2;i<=n;i++){
		if(!notPrime[i]){
			phi[i] = i-1;
			mu[i] = -1;
			prime.push_back(i);
		}
		for(auto p : prime){
			if(i*p > n) break;
			notPrime[i*p] = 1;
			if(i%p==0){
				phi[i*p] = p * phi[i];
				mu[i*p] = 0;
				break;
			}
			phi[i*p] = (p-1) * phi[i];
			mu[i*p] = -mu[i];
		}
	}
	for(int i=1;i<=n;i++){
		suphi[i] = suphi[i-1] + phi[i];
		sumu[i] = sumu[i-1] + mu[i];
	}
}

signed main(){
	euler(1e6);
	int T;cin>>T;
	while(T--){
		int x;cin>>x;
		cout<<djsphi(x)<<' '<<djsmu(x)<<'\n';
	}
}

posted @ 2025-07-12 10:51  fyv233  阅读(12)  评论(0)    收藏  举报