杜教筛
设要求积性函数
f
f
f 的前缀和, 设
S
(
n
)
=
∑
i
=
1
n
f
(
i
)
S(n)=\sum \limits_{i=1}^{n} f(i)
S(n)=i=1∑nf(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=1∑n(f∗g)=i=1∑nd∣i∑f(d)g(di)=d=1∑ni∣d∑g(d)f(di)=d=1∑ng(d)i=1∑⌊dn⌋f(i)=d=1∑ng(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=1∑1g(i)S(⌊in⌋)=i=1∑ng(i)S(⌊in⌋)−i=2∑ng(i)S(⌊in⌋)=i=1∑n(f∗g)(i)−i=2∑ng(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=1∑n(f∗g)(i)−i=2∑ng(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=1∑nΘ(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=1∑⌊mn⌋Θ(in)=Θ(mn)
当
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=1∑nμ(i),
1
⩽
n
⩽
2
31
1\leqslant n\leqslant2^{31}
1⩽n⩽231
首先,线筛肯定炸。
考虑到
μ
∗
1
=
ϵ
μ*1=ϵ
μ∗1=ϵ,令
f
=
μ
,
g
=
1
f=μ,g=1
f=μ,g=1,则
f
∗
g
=
ϵ
f*g=ϵ
f∗g=ϵ。
代入到上面的结论中。
μ
(
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=1∑nϵ(i)−i=2∑n1(i)S(⌊in⌋)=1−i=2∑nS(⌊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=1∑nφ(i),
1
⩽
n
⩽
2
31
1\leqslant n\leqslant2^{31}
1⩽n⩽231
考虑到
φ
∗
1
=
i
d
φ*1=id
φ∗1=id,则选择
f
=
φ
,
g
=
1
f=\varphi,g=1
f=φ,g=1,则
f
∗
g
=
i
d
f*g=id
f∗g=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=1∑n(f∗g)(i)−i=2∑ng(i)S(⌊in⌋)=i=1∑ni−i=2∑nS(⌊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=1∑nφ(i)=21(1+i=1∑n⌊in⌋⌊in⌋μ(i))
证明 可以发现,
∑
i
=
1
n
φ
(
i
)
\begin{aligned}\sum_{i=1}^n\varphi(i)\end{aligned}
i=1∑nφ(i) 其实就是
1
∼
n
1\sim n
1∼n 中互质的数的对数。去除重复计算和只出现了一次的
(
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=1∑nφ(i)=21(1+i=1∑nj=1∑n[(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=1∑nj=1∑n[(i,j)=1]=i=1∑n⌊in⌋⌊in⌋μ(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 |