杜教筛(SUM)

文章详细介绍了数论函数的狄利克雷卷积的前缀和计算方法,以及如何利用杜教筛快速求解数论函数的前缀和,特别讨论了欧拉函数和莫比乌斯函数的例子,并分析了杜教筛的时间复杂度为O(n^(2/3))。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

设数论函数 g g g的前缀和为 s ( n ) s(n) s(n)

对于数论函数 f , g f,g f,g,求其狄利克雷卷积的前缀和:
∑ i = 1 n ( f ∗ g ) ( i ) \overset{n}{\underset{i=1}\sum}(f*g)(i) i=1n(fg)(i)
= ∑ i = 1 n ∑ d = 1 n f ( d ) g ( i d ) [ d ∣ i ] =\overset{n}{\underset{i=1}\sum}\overset{n}{\underset{d=1}\sum}f(d)g(\frac id)[d|i] =i=1nd=1nf(d)g(di)[di]
= ∑ d = 1 n ∑ i = 1 n [ d ∣ i ] f ( d ) g ( i d ) =\overset{n}{\underset{d=1}\sum}\overset{n}{\underset{i=1}\sum}[d|i]f(d)g(\frac id) =d=1ni=1n[di]f(d)g(di)
= ∑ d = 1 n f ( d ) ∑ i = 1 ⌊ n d ⌋ g ( i ) =\overset{n}{\underset{d=1}\sum}f(d)\overset{\left\lfloor\frac nd\right\rfloor}{\underset{i=1}\sum}g(i) =d=1nf(d)i=1dng(i)
= ∑ d = 1 n f ( d ) s ( ⌊ n d ⌋ ) =\overset{n}{\underset{d=1}\sum}f(d)s\left(\left\lfloor\frac nd\right\rfloor\right) =d=1nf(d)s(dn)
= ∑ d = 1 n f ( d ) s ( ⌊ n d ⌋ ) =\overset{n}{\underset{d=1}\sum}f(d)s\left(\left\lfloor\frac nd\right\rfloor\right) =d=1nf(d)s(dn)

这也就是说,若数论函数 f f f的前缀和为 n n n,则有:
∑ i = 1 n ( f ∗ g ) ( i ) = ∑ i = 1 n g ( i ) s ( ⌊ n i ⌋ ) = g ( 1 ) s ( n ) + ∑ i = 2 n g ( i ) s ( ⌊ n i ⌋ ) \overset{n}{\underset{i=1}\sum}(f*g)(i)=\overset{n}{\underset{i=1}\sum}g(i)s\left(\left\lfloor\frac ni\right\rfloor\right)=g(1)s(n)+\overset{n}{\underset{i=2}\sum}g(i)s\left(\left\lfloor\frac ni\right\rfloor\right) i=1n(fg)(i)=i=1ng(i)s(in)=g(1)s(n)+i=2ng(i)s(in)

因此:
g ( 1 ) s ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) s ( ⌊ n i ⌋ ) g(1)s(n)=\overset{n}{\underset{i=1}\sum}(f*g)(i)-\overset{n}{\underset{i=2}\sum}g(i)s\left(\left\lfloor\frac ni\right\rfloor\right) g(1)s(n)=i=1n(fg)(i)i=2ng(i)s(in)

杜教筛

杜教筛用于快速求数论函数前缀和

对于要求的函数 f f f,定义其前缀和为 s s s,则需要构造辅助函数 g g g,使得其狄利克雷卷积的前缀和与 g g g函数的点值容易求出。

经典的可以用杜教筛的函数有:
欧拉函数 φ \varphi φ φ ∗ 1 = i d \varphi*1=id φ1=id
莫比乌斯函数 μ \mu μ μ ∗ 1 = ε \mu*1=\varepsilon μ1=ε

过程借助递归式 g ( 1 ) s ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) s ( ⌊ n i ⌋ ) g(1)s(n)=\overset{n}{\underset{i=1}\sum}(f*g)(i)-\overset{n}{\underset{i=2}\sum}g(i)s\left(\left\lfloor\frac ni\right\rfloor\right) g(1)s(n)=i=1n(fg)(i)i=2ng(i)s(in)

杜教筛的过程是:

  1. 欧拉筛计算前 O ( n 2 3 ) O(n^{\frac 23}) O(n32) f , s f,s f,s
  2. 用哈希表存计算好的 s s s值来剪枝
  3. 用整除分块递归计算 s ( n ) s(n) s(n)

用主定理分析一波,可以得知,杜教筛的时间复杂度为 O ( n 2 3 ) O\left(n^{\frac 23}\right) O(n32)

具体做法是:

  • φ \varphi φ
    s ( n ) = ∑ i = 1 n ( φ ∗ 1 ) ( i ) − ∑ i = 2 n s ( ⌊ n i ⌋ ) s(n)=\overset{n}{\underset{i=1}\sum}(\varphi*1)(i)-\overset{n}{\underset{i=2}\sum}s\left(\left\lfloor\frac ni\right\rfloor\right) s(n)=i=1n(φ1)(i)i=2ns(in)
    = ∑ i = 1 n i − ∑ i = 2 n s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum} i-\overset{n}{\underset{i=2}\sum}s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1nii=2ns(in)
    前面这个…就需要依靠小学奥数了。

  • μ \mu μ
    s ( n ) = ∑ i = 1 n ( μ ∗ 1 ) ( i ) − ∑ i = 2 n s ( ⌊ n i ⌋ ) s(n)=\overset{n}{\underset{i=1}\sum}(\mu*1)(i)-\overset{n}{\underset{i=2}\sum}s\left(\left\lfloor\frac ni\right\rfloor\right) s(n)=i=1n(μ1)(i)i=2ns(in)
    = ∑ i = 1 n [ i = 1 ] − ∑ i = 2 n s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}[i=1]-\overset{n}{\underset{i=2}\sum}s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1n[i=1]i=2ns(in)

#include<iostream>
#include<unordered_map>
#include<vector>
using namespace std;
#define int long long
const int N=2e6;
long long phi[N+5],mu[N+5];
bool vis[N+5];
vector<int> sta;
void Euler() {
	mu[1]=1;
	phi[1]=1;
	for(int i=2;i<=N;i++) {
		if(!vis[i])
			mu[i]=-1,
			phi[i]=i-1,
			sta.push_back(i);
		for(auto&j:sta) {
			int m=i*j;
			if(m>N) break;
			vis[m]=true;
			if(i%j)
				mu[m]=-mu[i],
				phi[m]=phi[i]*phi[j];
			else {
				phi[m]=j*phi[i];
				break;
			}
		}
	}
	for(int i=1;i<=N;i++)
		mu[i]+=mu[i-1],
		phi[i]+=phi[i-1]; 
}
unordered_map<int,pair<long long,long long>> H;
pair<long long,long long> sum(int n) {
	if(n<=N) return {phi[n],mu[n]};
	if(H.count(n)) return H[n];
	pair<long long,long long> ans={(long long)n*(n+1)>>1,1},t;
	for(long long l=2,r;l<=n;l=r+1) 
		r=n/(n/l),
		t=sum(n/l),
		ans.first-=t.first*(r-l+1),
		ans.second-=t.second*(r-l+1);
	return H[n]=ans;
}
signed main() {
	Euler();
//	for(int i=1;i<=100;i++)
//		cout<<mu[i]<<' ';
	int T;
	cin>>T;
	while(T--) {
		int n;
		cin>>n;
		auto x=sum(n);
		cout<<x.first<<' '<<x.second<<endl;
	}
}

更进一步

杜教筛还可以预处理出 ϕ ( x ) = φ ( x ) ⋅ I d k ( x ) \phi(x)=\varphi(x)\cdot Id_k(x) ϕ(x)=φ(x)Idk(x) ϕ ( x ) = μ ( x ) ⋅ I d k ( x ) \phi(x)=\mu(x)\cdot Id_k(x) ϕ(x)=μ(x)Idk(x)的前缀和。

  • ϕ ( x ) = φ ( x ) ⋅ I d k ( x ) \phi(x)=\varphi(x)\cdot Id_k(x) ϕ(x)=φ(x)Idk(x)
    构造辅助函数 g = I d k g=Id_k g=Idk
    s ( n ) = ∑ i = 1 n ( ϕ ∗ I d k ) ( i ) − ∑ i = 2 n I d k ( i ) s ( ⌊ n i ⌋ ) s(n)=\overset{n}{\underset{i=1}\sum}(\phi*Id_k)(i)-\overset{n}{\underset{i=2}\sum}Id_k(i)s\left(\left\lfloor\frac ni\right\rfloor\right) s(n)=i=1n(ϕIdk)(i)i=2nIdk(i)s(in)
    = ∑ i = 1 n ∑ d ∣ i φ ( d ) d k ( i d ) k − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}\overset{}{\underset{d|i}\sum}\varphi(d)d^k\left(\frac i d\right)^k-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1ndiφ(d)dk(di)ki=2ndks(in)
    = ∑ i = 1 n i k ∑ d ∣ i φ ( d ) − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}i^k\overset{}{\underset{d|i}\sum}\varphi(d)-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1nikdiφ(d)i=2ndks(in)
    注意到有 ∑ d ∣ i φ ( d ) = i \overset{}{\underset{d|i}\sum}\varphi(d)=i diφ(d)=i
    = ∑ i = 1 n i k + 1 − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}i^{k+1}-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1nik+1i=2ndks(in)
    至于你怎么求出 x x x次方数的前缀和…这个就要找奥数老师了。=w=

  • ϕ ( x ) = μ ( x ) ⋅ I d k ( x ) \phi(x)=\mu(x)\cdot Id_k(x) ϕ(x)=μ(x)Idk(x)
    构造辅助函数 g = I d k g=Id_k g=Idk
    s ( n ) = ∑ i = 1 n ( ϕ ∗ I d k ) ( i ) − ∑ i = 2 n I d k ( i ) s ( ⌊ n i ⌋ ) s(n)=\overset{n}{\underset{i=1}\sum}(\phi*Id_k)(i)-\overset{n}{\underset{i=2}\sum}Id_k(i)s\left(\left\lfloor\frac ni\right\rfloor\right) s(n)=i=1n(ϕIdk)(i)i=2nIdk(i)s(in)
    = ∑ i = 1 n ∑ d ∣ i μ ( d ) d k ( i d ) k − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}\overset{}{\underset{d|i}\sum}\mu(d)d^k\left(\frac i d\right)^k-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1ndiμ(d)dk(di)ki=2ndks(in)
    = ∑ i = 1 n i k ∑ d ∣ i μ ( d ) − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}i^k\overset{}{\underset{d|i}\sum}\mu(d)-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1nikdiμ(d)i=2ndks(in)
    注意到有 ∑ d ∣ i μ ( d ) = [ i = 1 ] \overset{}{\underset{d|i}\sum}\mu(d)=[i=1] diμ(d)=[i=1]
    = ∑ i = 1 n i k [ i = 1 ] − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =\overset{n}{\underset{i=1}\sum}i^{k}[i=1]-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =i=1nik[i=1]i=2ndks(in)
    = 1 − ∑ i = 2 n d k ⋅ s ( ⌊ n i ⌋ ) =1-\overset{n}{\underset{i=2}\sum}d^k\cdot s\left(\left\lfloor\frac ni\right\rfloor\right) =1i=2ndks(in)

后记

于是皆大欢喜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值