杜教筛详解

前置技能 狄利克雷卷积 线性筛函数

杜教筛

设要求积性函数 f f f 的前缀和, 设 S ( n ) = ∑ i = 1 n f ( i ) S(n)=\sum \limits_{i=1}^{n} f(i) S(n)=i=1nf(i)
设另有一函数 g g g,则它们的狄利克雷卷积的前缀和为
∑ i = 1 n ( f ∗ g ) = ∑ i = 1 n ∑ d ∣ i f ( d ) g ( i d ) = ∑ d = 1 n ∑ i ∣ d g ( d ) f ( i d ) = ∑ d = 1 n g ( d ) ∑ i = 1 ⌊ n d ⌋ f ( i ) = ∑ d = 1 n g ( d ) S ( ⌊ n d ⌋ ) \begin{aligned} \sum_{i=1}^{n}(f*g)&= \sum\limits_{i=1}^{n} \sum \limits _{d|i} f(d)g(\frac{i}{d}) \\ &= \sum\limits_{d=1}^{n} \sum \limits _{i|d} g(d)f(\frac{i}{d}) \\ &= \sum _{d=1}^{n} g(d)\sum _{i=1}^{\large \lfloor\frac{n}{d}\rfloor } f(i) \\ &= \sum _{d=1}^{n} g(d) S(\lfloor \frac{n}{d} \rfloor) \end{aligned} i=1n(fg)=i=1ndif(d)g(di)=d=1nidg(d)f(di)=d=1ng(d)i=1dnf(i)=d=1ng(d)S(⌊dn⌋)

接下来考虑 g ( 1 ) S ( n ) g(1)S(n) g(1)S(n)
g ( 1 ) S ( n ) = ∑ i = 1 1 g ( i ) S ( ⌊ n i ⌋ ) = ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) \begin{aligned} g(1)S(n)&=\sum_{i=1}^1g(i)S(\lfloor\frac ni\rfloor)\\ &=\sum \limits _{i=1}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) - \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor)\\ &=\sum_{i=1}^n(f*g)(i)- \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) \end{aligned} g(1)S(n)=i=11g(i)S(⌊in⌋)=i=1ng(i)S(⌊in⌋)i=2ng(i)S(⌊in⌋)=i=1n(fg)(i)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)=\sum_{i=1}^n(f*g)(i)- \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor) g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(⌊in⌋)

这让我们开始考虑是否有某些十分容易得到前缀和的积性函数,如果有这样的积性函数的话,那右边那个式子可以使用除法分块解决。令求出 S ( n ) S(n) S(n) 的时间复杂度为 T ( n ) T(n) T(n),若左边的式子可以 Θ ( 1 ) \Theta(1) Θ(1) 求解,考虑到除法分块的时间复杂度为 Θ ( n ) \Theta(\sqrt n) Θ(n ),有:
T ( n ) = ∑ i = 1 n Θ ( i ) + Θ ( n i ) = Θ ( n 3 4 ) T(n) = \sum\limits_{i=1}^{\sqrt n} \Theta(\sqrt i) + \Theta\bigg(\sqrt {\frac{n}{i}}\bigg)=\Theta\big(n^{\frac{3}{4}}\big) T(n)=i=1n Θ(i )+Θ(in )=Θ(n43)

如果我们先预处理出前 m m m 个前缀和,则时间复杂度为
T ( n ) = ∑ i = 1 ⌊ n m ⌋ Θ ( n i ) = Θ ( n m ) T(n) = \sum\limits_{i=1}^{\lfloor \frac{n}{m} \rfloor} \Theta\bigg(\sqrt \frac{n}{i}\bigg) = \Theta \Big({\frac{n}{\sqrt m}}\Big) T(n)=i=1mnΘ(in )=Θ(m n)

m = n 2 3 m=n^{\frac 23} m=n32 时, T ( n ) = Θ ( n 2 3 ) T(n) = \Theta(n^{\frac{2}{3}}) T(n)=Θ(n32) ,时间复杂度达到最低。
可惜上述时间复杂度都必须建立在一个基础条件下:记忆化 ,如果不这样做,时间复杂度无法保证,可是程序不允许开如此大的数组,如果使用 m a p map map,则程序的时间复杂度会变为多一个 l o g 2 n log_2n log2n
其实并不需要使用 m a p map map,考虑到每次递归求解时,我们需要的并不是 S ( n ) S(n) S(n) 的值,而是 S ( ⌊ n i ⌋ ) S(\lfloor\large \frac ni\normalsize\rfloor) S(⌊in⌋) 的值,因此可以仅仅用 S [ i ] S[i] S[i] 保存 S ( ⌊ n i ⌋ ) S(\lfloor\large \frac ni\normalsize\rfloor) S(⌊in⌋)
代码如下:

long long S(int n) {
	if(n<maxm) return mSphi[n];//线性筛
	int x=maxn/n;//求出S(n)应该放在的位置
	if(vis[x]) return Sphi[x];//记忆化
	vis[x]=true;
	long long &ans=Sphi[x]=calc_left();//左边的答案
	for (int i=2,j;i<=n;i=j+1) {
		j=n/(n/i);
		ans-=(j-i+1)*S(n/i)*g(i,j);//g(i,j)表示 $\sum g(i)$
	} return ans;
}

应用

μ \mu μ 的前缀和

∑ i = 1 n μ ( i ) \begin{aligned}\sum_{i=1}^n\mu(i)\end{aligned} i=1nμ(i) 1 ⩽ n ⩽ 2 31 1\leqslant n\leqslant2^{31} 1n231
首先,线筛肯定炸。
考虑到 μ ∗ 1 = ϵ μ*1=ϵ μ1=ϵ,令 f = μ , g = 1 f=μ,g=1 f=μ,g=1,则 f ∗ g = ϵ f*g=ϵ fg=ϵ
代入到上面的结论中。
μ ( 1 ) S ( n ) = ∑ i = 1 n ϵ ( i ) − ∑ i = 2 n 1 ( i ) S ( ⌊ n i ⌋ ) S ( n ) = 1 − ∑ i = 2 n S ( ⌊ n i ⌋ ) \begin{aligned}\mu(1)S(n)&=\sum_{i=1}^nϵ(i)-\sum_{i=2}^n1(i)S(\lfloor \frac{n}{i} \rfloor)\\ S(n)&=1-\sum_{i=2}^nS(\lfloor\frac ni\rfloor) \end{aligned} μ(1)S(n)S(n)=i=1nϵ(i)i=2n1(i)S(⌊in⌋)=1i=2nS(⌊in⌋)
然后就很愉快了。

const int maxn=2147483647;
const int maxm=2000000;
long long mSmu[maxm+10],Smu[1300];
bool vis[1300];
long long S(int n) {
	if(n<maxm) return mSmu[n];
	int x=maxn/n;
	if(vis[x]) return Smu[x];
	vis[x]=true;
	long long &ans=Smu[x]=1;
	for (int l=2,r;l<=n;l=r+1) {
		r=n/(n/l);
		ans-=(r-l+1)*S(n/l);
	} return ans;
}
const int maxn=2147483647;
const int maxm=2000000;
long long mSmu[maxm+10];
bool vis[maxm];map<int,int> Smu;
long long S(int n) {
	if(n<maxm) return mSmu[n];
	if(Smu.find(n)!=Smu.end()) return Smu[n];
	int &ans=(Smu[n]=1);
	for (int l=2,r;l<=n;l=r+1) {
		r=n/(n/l);
		ans-=(r-l+1)*S(n/l);
	} return ans;
}
φ \varphi φ 的前缀和

∑ i = 1 n φ ( i ) \begin{aligned}\sum_{i=1}^n\varphi(i)\end{aligned} i=1nφ(i) 1 ⩽ n ⩽ 2 31 1\leqslant n\leqslant2^{31} 1n231
考虑到 φ ∗ 1 = i d φ*1=id φ1=id,则选择 f = φ , g = 1 f=\varphi,g=1 f=φ,g=1,则 f ∗ g = i d f*g=id fg=id,代入。
g ( 1 ) S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) S ( n ) = ∑ i = 1 n i − ∑ i = 2 n S ( ⌊ n i ⌋ ) \begin{aligned}g(1)S(n)&=\sum_{i=1}^n(f*g)(i)- \sum \limits _{i=2}^{n} g(i) S(\lfloor \frac{n}{i} \rfloor)\\ S(n)&=\sum_{i=1}^ni-\sum_{i=2}^nS(\lfloor \frac{n}{i} \rfloor)\\ \end{aligned} g(1)S(n)S(n)=i=1n(fg)(i)i=2ng(i)S(⌊in⌋)=i=1nii=2nS(⌊in⌋)
前面可以根据小学奥数得到,后面除法分块即可,代码如下:

const int maxn=2147483647;
const int maxm=2000000;
long long mSphi[maxm+10],Sphi[1300];
bool vis[1300];
long long Sum_phi(int n) {
	if(n<maxm) return mSphi[n];
	int x=maxn/n;
	if(vis[x]) return Sphi[x];
	vis[x]=true;
	long long &ans=Sphi[x]=(long long)(1+n)*n/2;
	for (int l=2,r;l<=n;l=r+1) {
		r=n/(n/l);
		ans-=(r-l+1)*S(n/l);
	} return ans;
}
const int maxn=2147483647;
const int maxm=2000000;
long long mSphi[maxm+10];
bool vis[maxm];map<int,long long> phi;
long long Sum_phi(int n) {
	if(n<maxm) return mSphi[n];
	if(Sphi.count(n)!=Sphi.end()) return Sphi[n];
	long long &ans=Sphi[n]=(long long)(1+n)*n/2;
	for (int l=2,r;l<=n;l=r+1) {
		r=n/(n/l);
		ans-=(r-l+1)*S(n/l);
	} return ans;
}

如果题目同时要求 μ \mu μ φ \varphi φ 呢?用杜教筛求出 μ \mu μ 之后其实不需要再用杜教筛求 φ \varphi φ
引理  ∑ i = 1 n φ ( i ) = 1 2 ( 1 + ∑ i = 1 n ⌊ n i ⌋ ⌊ n i ⌋ μ ( i ) ) \begin{aligned}\sum\limits_{i=1}^n\varphi(i)=\dfrac{1}{2}\bigg(1+\sum_{i=1}^n\lfloor\large\frac ni\normalsize\rfloor\lfloor\large\frac ni\normalsize\rfloor\mu(i)\bigg)\end{aligned} i=1nφ(i)=21(1+i=1nininμ(i))
证明 可以发现, ∑ i = 1 n φ ( i ) \begin{aligned}\sum_{i=1}^n\varphi(i)\end{aligned} i=1nφ(i) 其实就是 1 ∼ n 1\sim n 1n 中互质的数的对数。去除重复计算和只出现了一次的 ( 1 , 1 ) (1,1) (1,1),可以得到下面这样的式子:
∑ i = 1 n φ ( i ) = 1 2 ( 1 + ∑ i = 1 n ∑ j = 1 n [ ( i , j ) = 1 ] ) \sum_{i=1}^n\varphi(i)=\frac 12\bigg(1+\sum_{i=1}^n\sum_{j=1}^n[(i,j)=1]\bigg) i=1nφ(i)=21(1+i=1nj=1n[(i,j)=1])

根据莫比乌斯反演,得:
∑ i = 1 n ∑ j = 1 n [ ( i , j ) = 1 ] = ∑ i = 1 n ⌊ n i ⌋ ⌊ n i ⌋ μ ( i ) \sum_{i=1}^n\sum_{j=1}^n[(i,j)=1]=\sum_{i=1}^n\lfloor\frac ni\rfloor\lfloor\frac ni\rfloor\mu(i) i=1nj=1n[(i,j)=1]=i=1nininμ(i)

回代即可。
因此我们得到了一种更好的计算 φ \varphi φ 的方法——整除分块。
注意不需要记忆化,因为本来就没有递归计算。

long long Sum_phi(int n) {
    if(n<maxm) return mSphi[n];
    long long phi=0;
    for(long long l=1,r;l<=n;l=r+1){
    	r=n/(n/l);
    	phi+=(long long)(n/l)*(n/l)*(Sum_mu(r)-Sum_mu(l-1));
	} return (phi+1)/2;
}

上面代码的时间复杂度似乎是 Θ ( n × n 2 3 ) = Θ ( n 1 2 × n 2 3 ) = Θ ( n 7 6 ) > Θ ( n ) \Theta(\sqrt n\times n^{\large\frac 23})=\Theta(n^{\large\frac 12}\times n^{\large\frac 23})=\Theta(n^{\large\frac{7}{6}})>\Theta(n) Θ(n ×n32)=Θ(n21×n32)=Θ(n67)>Θ(n) 的,根本无法通过测试。但是考虑到每次杜教筛是记忆化的。因而相当于只考虑了一次杜教筛,即 Θ ( n 1 2 + n 2 3 ) < Θ ( n ) \Theta(n^{\large\frac 12}+n^{\large\frac23})<\Theta(n) Θ(n21+n32)<Θ(n)。这也奠定了杜教筛在莫比乌斯反演中的作用,保证下面题表中所有的算法的时间复杂度的正确。

题表

题目题解
Luogu P3768 简单的数学题欧拉反演,杜教筛 φ ∗ i d 2 \varphi*id^2 φid2
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值