杜教筛
首先我们要知道杜教筛是用来求积性函数的前缀和的。
前置知识1:积性函数
若
a
a
a和
b
b
b互质,那么有
f
(
a
b
)
=
f
(
a
)
f
(
b
)
f(ab) = f(a)f(b)
f(ab)=f(a)f(b),则称
f
(
x
)
f(x)
f(x)为积性函数。
若函数
f
(
x
)
f(x)
f(x),对于任意两个数
a
a
a和
b
b
b,都有
f
(
a
b
)
=
f
(
a
)
f
(
b
)
f(ab)=f(a)f(b)
f(ab)=f(a)f(b),则称
f
(
x
)
f(x)
f(x)为完全积性函数
常见的积性函数
- 欧拉函数 φ ( x ) \varphi (x) φ(x) 比 x x x小的与 x x x互质的数的个数
- 莫比乌斯函数 μ ( x ) = { 1 , x = 1 ( − 1 ) k , x = p 1 p 2 ⋯ p k , p i 为 不 相 等 的 素 数 0 , x 有 大 于 1 的 平 方 数 因 数 \mu (x) =\left\{\begin{matrix} 1, x = 1\\ (-1)^k,x=p_1p_2\cdots p_k,p_i为不相等的素数\\ 0,x有大于1的平方数因数 \end{matrix}\right. μ(x)=⎩⎨⎧1,x=1(−1)k,x=p1p2⋯pk,pi为不相等的素数0,x有大于1的平方数因数
- 正因子和 σ ( x ) = ∑ d ∣ x d \sigma (x) = \sum_{d|x}d σ(x)=∑d∣xd
- 正因子个数 σ ( x ) = ∑ d ∣ x 1 \sigma (x) = \sum_{d|x}1 σ(x)=∑d∣x1
常见的完全积性函数
- ϵ ( x ) = [ x = = 1 ] \epsilon (x) = [x == 1] ϵ(x)=[x==1]
- I ( x ) = 1 I(x) = 1 I(x)=1
- i d ( x ) = x id(x) = x id(x)=x
前置知识2:狄利克雷卷积
定义: 对于两个积性函数 f ( n ) g ( n ) f(n) g(n) f(n)g(n)的狄利克雷卷积为 ( f ∗ g ) ( i ) = ∑ d ∣ n f ( d ) ∗ g ( n d ) (f*g)(i) = \sum_{d|n}f(d)*g(\frac{n}{d}) (f∗g)(i)=∑d∣nf(d)∗g(dn),也就是对于 i ∗ j = = n i * j == n i∗j==n的整数 i , j i,j i,j的函数值乘在一起再求和。
狄利克雷卷积满足结合律和交换律
单位函数
ϵ
(
x
)
\epsilon (x)
ϵ(x)是狄利克雷卷积的单位元,任何函数卷上
ϵ
(
x
)
\epsilon (x)
ϵ(x)都是它本身
一些狄利克雷卷积的结论:
1.
μ
∗
I
=
ϵ
(
x
)
\mu * I = \epsilon(x)
μ∗I=ϵ(x)
2.
φ
∗
I
=
i
d
(
x
)
\varphi * I = id(x)
φ∗I=id(x)
3.
i
d
∗
μ
=
φ
(
x
)
id * \mu = \varphi(x)
id∗μ=φ(x)
前置知识3:莫比乌斯反演
对弈两个积性函数如果有
f
=
g
∗
I
f = g * I
f=g∗I, 则有
g
=
f
∗
μ
g = f * \mu
g=f∗μ
把卷积拆开,原式为
f
(
x
)
=
∑
d
∣
x
g
(
d
)
f(x) = \sum_{d|x}g(d)
f(x)=∑d∣xg(d),则有
g
(
x
)
=
∑
d
∣
x
f
(
x
d
)
∗
μ
(
d
)
g(x) = \sum_{d|x}f(\frac{x}{d})*\mu(d)
g(x)=∑d∣xf(dx)∗μ(d)
证明:
f
=
g
∗
I
f = g * I
f=g∗I
f
∗
μ
=
g
∗
I
∗
μ
f * \mu = g * I * \mu
f∗μ=g∗I∗μ
f
∗
μ
=
g
∗
ϵ
f * \mu = g * \epsilon
f∗μ=g∗ϵ
f
∗
μ
=
g
f * \mu = g
f∗μ=g
杜教筛
我们构造积性函数
f
,
g
f,g
f,g,令
S
(
n
)
=
∑
i
=
1
n
f
(
i
)
S(n) = \sum_{i = 1}^n f(i)
S(n)=∑i=1nf(i)
S
(
n
)
=
∑
i
=
1
n
(
f
∗
g
)
(
i
)
−
∑
i
=
2
n
g
(
i
)
∗
S
(
n
i
)
g
(
1
)
S(n) = \frac{\sum_{i = 1}^{n}(f*g)(i) - \sum_{i = 2}^{n}g(i)*S(\frac{n}{i})}{g(1)}
S(n)=g(1)∑i=1n(f∗g)(i)−∑i=2ng(i)∗S(in)
称此公式为杜教筛公式
证明略
这里放一道洛谷的模板题:传送门
题中的
a
n
s
1
=
∑
i
=
1
n
φ
(
i
)
ans_1 = \sum_{i = 1}^{n} \varphi (i)
ans1=∑i=1nφ(i)
我们令
S
(
n
)
=
∑
i
=
1
n
φ
(
i
)
S(n) = \sum_{i = 1}^{n} \varphi (i)
S(n)=i=1∑nφ(i)
我们令
g
=
I
g = I
g=I
代入杜教筛公式
S
(
n
)
=
∑
i
=
1
n
ϵ
(
i
)
−
∑
i
=
2
n
I
(
i
)
∗
S
(
n
i
)
1
S(n) = \frac{\sum_{i = 1}^{n}\epsilon (i) - \sum_{i = 2}^{n} I(i) * S(\frac{n}{i}) }{1}
S(n)=1∑i=1nϵ(i)−∑i=2nI(i)∗S(in)
S
(
n
)
=
1
−
∑
i
=
2
n
∗
S
(
n
i
)
S(n) = 1 - \sum_{i = 2}^{n} * S(\frac{n}{i})
S(n)=1−i=2∑n∗S(in)
然后就用数论分块模板求
S
(
n
)
S(n)
S(n)了,但直接这样求显然是做不到线性的复杂度的,所以我们需要预处理出
μ
(
i
)
φ
(
i
)
\mu(i) \varphi(i)
μ(i)φ(i),然后对于求过的答案用
m
a
p
map
map记录来降低时间复杂度。经过证明,当预处理的值为
n
2
3
n^{\frac{2}{3}}
n32时,时间复杂度
O
(
n
2
3
)
O(n^{\frac{2}{3}})
O(n32)。
a
n
s
2
ans2
ans2也可以用同样的方法进行化简,最后的式子为
S
(
n
)
=
n
(
n
+
1
)
2
−
∑
i
=
2
n
∗
S
(
n
i
)
S(n) = \frac{n(n + 1)}{2} - \sum_{i = 2}^{n} * S(\frac{n}{i})
S(n)=2n(n+1)−i=2∑n∗S(in)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
//#define LL __int128;
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define fi first
#define se second
#define pb push_back
#define mp(a,b) make_pair(a,b)
#define PAUSE system("pause");
const double eps = 1e-8;
const int maxn = 5e6 + 10;
const int mod = 1e9 + 7;
int prime[maxn], mu[maxn], check[maxn];
ll phi[maxn];
unordered_map<ll, int> ans_mu;
unordered_map<ll, ll> ans_phi;
void init() {
mu[1] = 1;
phi[1] = 1;
int tot = 0;
for(int i = 2; i <= maxn; i++) {
if(!check[i]) {
prime[tot++] = i;
mu[i] = -1;
phi[i] = i - 1;
}
for(int j = 0; j < tot; j++) {
if(i * prime[j] > maxn) break;
check[i * prime[j]] = true;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else{
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
mu[i * prime[j]] = -mu[i];
}
}
}
for(int i = 1; i <= maxn; i++) {
mu[i] += mu[i - 1];
phi[i] += phi[i - 1];
}
}
int get_mu(ll x) {
if(x <= maxn) return mu[x];
if(ans_mu[x]) return ans_mu[x];
int ans = 1;
for(ll l = 2, r; l <= x; l = r + 1){
r = x / (x / l);
ans -= (r - l + 1) * get_mu(x / l);
}
ans_mu[x] = ans;
return ans;
}
ll get_phi(ll x) {
if(x <= maxn) return phi[x];
if(ans_phi[x]) return ans_phi[x];
ll ans = (ll)(x + 1) * (ll)x / 2;
for(ll l = 2, r; l <= x; l = r + 1){
r = x / (x / l);
ans -= (r - l + 1) * get_phi(x / l);
}
ans_phi[x] = ans;
return ans;
}
void solve(){
ll n;
scanf("%lld", &n);
printf("%lld %d\n", get_phi(n), get_mu(n));
}
int main() {
init();
int t = 1;
scanf("%d", &t);
// cin>>t;
while(t--)
solve();
PAUSE;
return 0;
}