###前置知识

####积性函数

若函数 f(n)f(n) 满足当 gcd(i,j)=1gcd(i,j)=1 时, f(ij)=f(i)f(j)f(ij)=f(i)f(j),那么这个函数为积性函数

常见的积性函数有

1.μ(n)\mu(n) —莫比乌斯函数

2.ϕ(n)\phi(n) —欧拉函数

3.d(n)d(n) —约数个数

4.σ(n)\sigma(n) —约数和函数

####完全积性函数

待会儿会用到

1.ϵ(n)\epsilon(n) —元函数,满足 ϵ(n)=[n=1]\epsilon(n)=[n=1]

2.I(n)I(n) —恒等函数,恒等于1

3.id(n)id(n) —单位函数,id(n)=nid(n)=n

可能这时你会觉得没用,当时我也是这么觉得的,但是看完下面就不会这么觉得的

####迪利克雷卷积(*)

定义:两个数论函数 f,gf,g 的卷积为 (f∗g)(n)=∑d∣nf(d)×g(nd)(f*g)(n)=\sum\limits_{d|n}f(d)\times g(\frac{n}{d}),括号表示范围

显然,满足 f∗g=g∗f,(f∗g)∗h=f∗(g∗h),(f+g)∗h=f∗h+g∗hf*g=g*f,(f*g)*h=f*(g*h),(f+g)*h=f*h+g*h,即交换律,结合律,分配律

有了这个有什么用呢

之前证明过莫比乌斯反演,其实迪利克雷卷积也可以证明

证明

我们知道,莫比乌斯函数满足 ∑d∣nμ(d)=[n=1]\sum\limits_{d|n}\mu(d)=[n=1]

写成迪利克雷卷积的形式就是 μ∗I=ϵ\mu*I=\epsilon

F(n)=∑i∣df(i)⇒F=f∗I⇒F∗μ=f∗I∗μ=f∗ϵ=fF(n)=\sum\limits_{i|d}f(i)\Rightarrow F=f*I \Rightarrow F*\mu=f*I*\mu=f*\epsilon=f

即 F∗μ=fF*\mu=f,代入后可得 f(n)=∑i∣dμ(di)F(d)f(n)=\sum\limits_{i|d}\mu(\frac{d}{i})F(d)

证毕

同时,它还可以用来推欧拉函数与莫比乌斯函数的关系

可以知道欧拉函数有一个性质 ∑d∣nμ(d)=n\sum\limits_{d|n}\mu(d)=n

写成迪利克雷卷积的形式就是 ϕ∗I=id\phi*I=id

⇒ϕ∗I∗μ=id∗μ⇒ϕ∗ϵ=id∗μ\Rightarrow \phi*I*\mu=id*\mu\\\Rightarrow \phi*\epsilon=id*\mu

即 ϕ=id∗μ⇒ϕ(n)=∑d∣nid(nd)μ(d)=∑d∣nnμ(d)d\phi=id*\mu\Rightarrow \phi(n)=\sum\limits_{d|n}id(\frac{n}{d})\mu(d)=\sum\limits_{d|n}\frac{n\mu(d)}{d}

###杜教筛

说完上面那些,就可以开始讲杜教筛了

首先,杜教筛是以低于线性时间复杂度来求积性函数前缀和的筛法

即我们需要求的式子是 ∑i=1nf(i)\sum\limits_{i=1}^nf(i)

为了求这个东西,我们首先构造两个积性函数,使得 f∗g=hf*g=h

不妨令 S(n)=∑i=1nf(i)S(n)=\sum\limits_{i=1}^nf(i)

f∗g=h⇒∑i=1nh(i)=∑i=1n∑d∣ig(d)∗f(di)=∑d=1ng(d)∑i=1⌊nd⌋f(i)f*g=h\Rightarrow\sum\limits_{i=1}^nh(i)=\sum\limits_{i=1}^n\sum\limits_{d|i}g(d)*f(\frac{d}{i})\\=\sum\limits_{d=1}^ng(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}f(i)

即 ∑i=1nh(i)=∑d=1ng(d)S(⌊nd⌋)\sum\limits_{i=1}^nh(i)=\sum\limits_{d=1}^ng(d)S(\lfloor\frac{n}{d}\rfloor)

提出第一项得到 g(1)S(n)=∑i=1nh(i)−∑d=2ng(d)S(⌊nd⌋)g(1)S(n)=\sum\limits_{i=1}^nh(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor)

简而言之,就是用简单的去求复杂的,需要你定义的 h,gh,g 比较好求

###应用

####求 S(n)=∑i=1nμ(i)S(n)=\sum\limits_{i=1}^n\mu(i)

根据上述式子 g(1)S(n)=∑i=1nh(i)−∑d=2ng(d)S(⌊nd⌋)g(1)S(n)=\sum\limits_{i=1}^nh(i)-\sum\limits_{d=2}^ng(d)S(\lfloor\frac{n}{d}\rfloor),我们只需要找到一个 gg使得这个式子容易求

显然 μ∗I=ϵ\mu*I=\epsilon,不妨假设 f=μ,g=I,h=ϵf=\mu,g=I,h=\epsilon

⇒S(n)=1−∑d=2nS(⌊nd⌋)\Rightarrow S(n)=1-\sum\limits_{d=2}^nS(\lfloor\frac{n}{d}\rfloor)

这样就可已通过杜教筛求莫比乌斯函数前缀和

####求 S(n)=∑i=1nϕ(i)S(n)=\sum\limits_{i=1}^n\phi(i)

同上,可以找到 ϕ∗I=id\phi*I=id,假设 f=ϕ,g=I,h=idf=\phi,g=I,h=id

⇒S(n)=∑i=1ni−∑d=2nS(⌊nd⌋)\Rightarrow S(n)=\sum\limits_{i=1}^ni-\sum\limits_{d=2}^nS(\lfloor\frac{n}{d}\rfloor)

####求 S(n)=∑i=1niϕ(i)S(n)=\sum\limits_{i=1}^ni\phi(i)

相比之前的两个,这个无法直接看出需要配什么积性函数

考虑 h(n)=∑d∣n(d×ϕ(d))×g(nd)h(n)=\sum\limits_{d|n}(d\times\phi(d))\times g(\frac{n}{d})

想要把其中的 dd 给约掉,可以尝试令 g=idg=id

⇒∑d∣n(d×ϕ(d))×nd=∑d∣nϕ(d)×n=n∑d∣nϕ(d)=n2\Rightarrow\sum\limits_{d|n}(d\times\phi(d))\times \frac{n}{d}=\sum\limits_{d|n}\phi(d)\times n=n\sum\limits_{d|n}\phi(d)=n^2

似乎可以

代回上面的式子

S(n)=∑i=1ni2−∑d=2ndS(⌊nd⌋)S(n)=\sum\limits_{i=1}^ni^2-\sum\limits_{d=2}^ndS(\lfloor\frac{n}{d}\rfloor)

这样就可以通过整除分块快速求出了

###杜教筛的实现

先有线性筛求出一个大概的范围,然后剩下的递归处理,

【模板】杜教筛(Sum)

#include<iostream>
#include<cstdio>
#include<cmath>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int M = 6e6 + 5;

bool st[M];
int prime[M], tot, mu[M], sum1[M];
ll sum2[M], phi[M];
unordered_map<ll, ll> Phi;
unordered_map<int, int> Mu;

void init()
{
phi[1] = mu[1] = 1;
for (int i = 2; i < M; i++)
{
if (!st[i])
prime[++tot] = i, mu[i] = -1, phi[i] = i - 1;
for (int j = 1; i * prime[j] < M; j++)
{
st[i * prime[j]] = true;
if (i % prime[j] == 0)
{
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
mu[i * prime[j]] = -mu[i], phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
for (int i = 1; i < M; i++)
sum1[i] = sum1[i - 1] + mu[i], sum2[i] = sum2[i - 1] + phi[i];
}

ll Djphi(ll x)
{
if (x < M)
return sum2[x];
if (Phi[x])
return Phi[x];
ll res = (ll)x * (x + 1) / 2;
for (ll l = 2, r; l <= x; l = r + 1)
{
r = min(x, x / (x / l));
res -= (ll)(r - l + 1) * Djphi(x / l);
}
return Phi[x] = res;
}

int Djmu(ll x)
{
if (x < M)
return sum1[x];
if (Mu[x])
return Mu[x];
int res = 1;
for (ll l = 2, r;l <= x; l = r + 1)
{
r = min(x, (x / (x / l)));
res -= (ll)(r - l + 1) * Djmu(x / l);
}
return Mu[x] = res;
}

int main()
{
init();
int T;
scanf("%d", &T);
while (T--)
{
ll n;
scanf("%lld", &n);
printf("%lld %d\n", Djphi(n), Djmu(n));
}
return 0;
}