须知
此文是作者写线性筛的第二篇文章,再读之前最好对于线性筛有一定的基础和认识,这里是作者写线性筛的第一篇文章。
前置知识
- 线性筛质数
- gcd \gcd gcd
- 一些推式子能力
- 一点数学基础
这里有一些函数定义:
- φ ( n ) \varphi(n) φ(n):
- d ( n ) d(n) d(n)
- ( n ) (n) (n)
基础题(复习)
求
F
(
n
)
=
∑
i
=
1
n
n
gcd
(
i
,
n
)
F(n)=\sum_{i=1}^n\frac{n}{\gcd(i,n)}
F(n)=∑i=1ngcd(i,n)n
输入格式:
- 第一行,一个整数 T T T。表示有 T T T 组测试数据。
- 接下来有
T
T
T 行,每行一个整数
n
n
n。
全部数据: -
1
≤
T
≤
1
0
6
,
1
≤
n
≤
1
0
7
1\le T\le10^6,1\le n\le10^7
1≤T≤106,1≤n≤107
输出格式: - 共 T T T 行,每行一个整数。
输入/输出例子
输入:
10
39
12
37
15
91
63
32
87
28
7
输出:
1099
77
1333
147
6751
2623
683
5691
473
43
样例解释
无
分析
考虑枚举
gcd
(
i
,
n
)
\gcd(i,n)
gcd(i,n),则:
F
(
n
)
=
∑
d
∣
n
n
d
∑
i
=
1
n
[
gcd
(
i
,
n
)
=
d
]
=
∑
d
∣
n
n
d
∑
i
=
1
n
d
[
gcd
(
i
,
n
d
)
=
1
]
=
∑
d
∣
n
n
d
φ
(
n
d
)
=
∑
d
∣
n
d
φ
(
d
)
\large{F(n)=\sum_{d|n}\frac{n}{d}\sum_{i=1}^n[\gcd(i,n)=d]=\sum_{d|n}\frac{n}{d}\sum_{i=1}^\frac{n}{d}[\gcd(i,\frac{n}{d})=1]=\sum_{d|n}\frac{n}{d}\varphi(\frac{n}{d})=\sum_{d|n}d\varphi(d)}
F(n)=d∣n∑dni=1∑n[gcd(i,n)=d]=d∣n∑dni=1∑dn[gcd(i,dn)=1]=d∣n∑dnφ(dn)=d∣n∑dφ(d)
F
(
n
)
F(n)
F(n)为积性函数
因为
φ
(
n
)
\varphi(n)
φ(n) 为积性函数,所以显然
F
(
n
)
F(n)
F(n)也是。
线性筛
- f ( 1 ) = 1 f(1)=1 f(1)=1
- 若 n n n 是质数,则 f ( n ) = 1 + ∑ i = 1 n − 1 n = n ( n − 1 ) + 1 f(n)=1+\sum_{i=1}^{n-1}n=n(n-1)+1 f(n)=1+∑i=1n−1n=n(n−1)+1
- 若 q q q 是质数且 q ∤ n q\nmid n q∤n,则 f ( q n ) = f ( q ) f ( n ) f(qn)=f(q)f(n) f(qn)=f(q)f(n)
- 若 q q q 是质数且 q ∣ n q\mid n q∣n,则: { q n = p k → f ( q n ) = f ( n ) + q n φ ( q n ) q n ≠ p k → f ( q n ) = f ( x ) f ( q n x ) , 其中 x 为最小质因子的最高次幂的值 \left\{\begin{matrix} qn=p^k\rightarrow f(qn)=f(n)+qn\varphi(qn) \\ qn\ne p^k\rightarrow f(qn)=f(x)f(\frac{qn}{x}),\text{其中}x\text{为最小质因子的最高次幂的值} \end{matrix}\right. {qn=pk→f(qn)=f(n)+qnφ(qn)qn=pk→f(qn)=f(x)f(xqn),其中x为最小质因子的最高次幂的值
参考代码:
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=1e7+1,M=2e6+1,mod=1e9+7;
int t,n,m,cnt;
struct fy{
int prv[N],phi[N],f[N],mx[N];
bool pr[N];
int getphi(int x){
int res=x;
for(int i=2;i*i<=x;i++){
if(x%i==0)
res=res*(i-1)/i;
while(x%i==0)
x/=i;
}
if(x>1)
res=res*(x-1)/x;
return res;
}
void ola(int x){
pr[1]=phi[1]=f[1]=1;
for(int i=1;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,phi[i]=i-1,f[i]=i*(i-1)+1,mx[i]=i;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
phi[u]=phi[i]*prv[j];
mx[u]=mx[i]*prv[j];
f[u]=(mx[u]==u?f[i]+phi[u]*u:f[mx[u]]*f[u/mx[u]]);
break;
}
else{
phi[u]=phi[i]*(prv[j]-1);
mx[u]=prv[j];
f[u]=f[i]*f[prv[j]];
}
}
}
}
}A;
signed main(){
IOS;
A.ola(N-1);
cin>>t;
while(t--)
cin>>n,cout<<A.f[n]<<"\n";
return 0;
}
莫比乌斯反演
莫反我自认为还是一个神奇的东西,本文对其的证明可能不好,如有错误请指出。
莫比乌斯函数
定义
考虑两个函数
f
(
x
)
,
g
(
x
)
f(x),g(x)
f(x),g(x) 满足
f
(
n
)
=
∑
d
∣
n
g
(
d
)
f(n)=\sum_{d|n}g(d)
f(n)=∑d∣ng(d)
易证,一定存在某个函数
h
(
x
)
h(x)
h(x),使得
g
(
n
)
=
∑
d
∣
n
f
(
d
)
h
(
d
)
g(n)=\sum_{d|n}f(d)h(d)
g(n)=∑d∣nf(d)h(d)
然后我们发现,对于一个定值
d
d
d,如果
n
n
n 不同,
h
(
d
)
h(d)
h(d) 的值也不同。(如
g
1
=
f
1
,
g
2
=
f
2
−
f
1
g_1=f_1,g_2=f_2-f_1
g1=f1,g2=f2−f1)
但是呢,
h
(
d
)
h(d)
h(d) 的值与
n
d
\frac{n}{d}
dn 有关,所以我们可以把
h
(
d
)
h(d)
h(d) 看成
μ
(
n
d
)
\mu(\frac{n}{d})
μ(dn),即
h
(
d
)
=
μ
(
n
d
)
h(d)=\mu(\frac{n}{d})
h(d)=μ(dn),其中
μ
\mu
μ 为一个函数。
所以可得
g
(
n
)
=
∑
d
∣
n
f
(
d
)
μ
(
n
d
)
g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d})
g(n)=∑d∣nf(d)μ(dn)
这个
μ
\mu
μ 就是莫比乌斯函数了。
它是这么定义的:
μ
(
x
)
=
{
1
x
=
1
0
∃
p
∈
P
r
i
m
e
,
p
2
∣
x
(
−
1
)
k
x
=
∏
i
=
1
k
p
i
(
质因数分解
)
\mu(x)=\left\{\begin{matrix} 1 & x=1\\ 0 & \exists p\in Prime,p^2|x\\ (-1)^k & x=\prod_{i=1}^kp_i(\text{质因数分解} ) \end{matrix}\right.
μ(x)=⎩
⎨
⎧10(−1)kx=1∃p∈Prime,p2∣xx=∏i=1kpi(质因数分解)
性质
- μ \mu μ 是积性函数。
- ∀ n ∈ N + \forall n\in N_+ ∀n∈N+, ∑ d ∣ n μ ( d ) = [ n = 1 ] \sum_{d|n}\mu(d)=[n=1] ∑d∣nμ(d)=[n=1]
证明:
1 :
- 若 x , y x,y x,y 中有一个 1 1 1,易证。
- 若 μ ( x ) , μ ( y ) \mu(x),\mu(y) μ(x),μ(y) 中有一个 0 0 0,易证。
- 否则 μ ( x y ) = ( − 1 ) c n t x ( − 1 ) c n t y = μ ( x ) μ ( y ) \mu(xy)=(-1)^{cnt_x}(-1)^{cnt_y}=\mu(x)\mu(y) μ(xy)=(−1)cntx(−1)cnty=μ(x)μ(y)
2 :
- 若 n = 1 n=1 n=1,易证。
- 否则设 n = ∏ i = 1 q p i x i n=\prod_{i=1}^qp_i^{x_i} n=∏i=1qpixi,根据 μ \mu μ 的定义,我们只考虑含有每个质因数一次的 k ( k ∣ n ) k(k|n) k(k∣n),算 k k k 的贡献。即: ∑ d ∣ n μ ( d ) = μ ( 1 ) + ∑ i = 1 q C q i ( − 1 ) i 1 q − i = C q 0 ( − 1 ) 0 1 q + ∑ i = 1 q C q i ( − 1 ) i 1 q − i = ∑ i = 0 q C q i ( − 1 ) i 1 q − i = ( − 1 + 1 ) q = 0 \sum_{d|n}\mu(d)=\mu(1)+\sum_{i=1}^qC_q^i(-1)^i1^{q-i}=C_q^0(-1)^01^q+\sum_{i=1}^qC_q^i(-1)^i1^{q-i}=\sum_{i=0}^qC_q^i(-1)^i1^{q-i}=(-1+1)^q=0 d∣n∑μ(d)=μ(1)+i=1∑qCqi(−1)i1q−i=Cq0(−1)01q+i=1∑qCqi(−1)i1q−i=i=0∑qCqi(−1)i1q−i=(−1+1)q=0
线性筛
其实根据定义就可以筛了。
参考代码:
void ola(int x){
pr[1]=mu[1]=1;
for(int i=2;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
mu[u]=0;
break;
}
else{
mu[u]=-mu[i];
}
}
}
}
狄利克雷卷积
狄利克雷卷积可以理解为一种定义新运算,符号是
∗
*
∗
考虑两个函数
f
(
x
)
,
g
(
x
)
f(x),g(x)
f(x),g(x),则
(
f
∗
g
)
(
x
)
=
∑
d
∣
x
f
(
d
)
g
(
x
d
)
(f*g)(x)=\sum_{d|x}f(d)g(\frac{x}{d})
(f∗g)(x)=∑d∣xf(d)g(dx)
这就是狄利克雷卷积,其中狄利克雷卷积包含以下性质:
- a ∗ b = b ∗ a a*b=b*a a∗b=b∗a
- a ∗ b ∗ c = a ∗ ( b ∗ c ) a*b*c=a*(b*c) a∗b∗c=a∗(b∗c)
整除分块
例:
求
∑
k
=
1
n
⌊
n
k
⌋
\sum_{k=1}^n \left \lfloor \frac{n}{k} \right \rfloor
∑k=1n⌊kn⌋
直接上代码了,请自行理解一下啊。
Code:
for(int l=1,r;l<=n;l=r+1){
r=(n/(n/l));
ans+=(r-l+1)*(n/l);
}
时间复杂度 O ( n ) {\color{red}\text{时间复杂度} O(\sqrt n)} 时间复杂度O(n)
莫比乌斯反演
考虑两个函数
f
(
x
)
,
g
(
x
)
f(x),g(x)
f(x),g(x) 满足
f
(
n
)
=
∑
d
∣
n
g
(
d
)
f(n)=\sum_{d|n}g(d)
f(n)=∑d∣ng(d),则
g
(
n
)
=
∑
d
∣
n
f
(
d
)
μ
(
n
d
)
g(n)=\sum_{d|n}f(d)\mu(\frac{n}{d})
g(n)=∑d∣nf(d)μ(dn)
正确性证明:
定义函数:
I
(
n
)
=
1
,
ϵ
(
n
)
=
[
n
=
1
]
,
∗
I(n)=1,\epsilon(n)=[n=1],*
I(n)=1,ϵ(n)=[n=1],∗为狄利克雷卷积符号
∴
μ
∗
I
=
ϵ
,
h
∗
ϵ
=
h
\therefore \mu*I=\epsilon,h*\epsilon=h
∴μ∗I=ϵ,h∗ϵ=h
∵
f
=
g
∗
I
\because f=g*I
∵f=g∗I
∴
f
∗
μ
=
g
∗
I
∗
μ
→
f
∗
μ
=
g
∗
(
I
∗
μ
)
→
g
∗
ϵ
=
f
∗
μ
→
g
=
f
∗
μ
\therefore f*\mu=g*I*\mu\rightarrow f*\mu=g*(I*\mu)\rightarrow g*\epsilon=f*\mu\rightarrow g=f*\mu
∴f∗μ=g∗I∗μ→f∗μ=g∗(I∗μ)→g∗ϵ=f∗μ→g=f∗μ
得证
例题
题目
题意:给定
n
,
m
,
k
n,m,k
n,m,k,求
∑
i
=
1
n
∑
j
=
1
m
[
gcd
(
i
,
j
)
=
k
]
\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]
∑i=1n∑j=1m[gcd(i,j)=k]
令
n
=
⌊
n
k
⌋
,
m
=
⌊
m
k
⌋
n=\left \lfloor \frac{n}{k} \right \rfloor,m=\left \lfloor \frac{m}{k} \right \rfloor
n=⌊kn⌋,m=⌊km⌋
则
a
n
s
=
∑
i
=
1
n
∑
j
=
1
m
[
gcd
(
i
,
j
)
=
1
]
ans=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]
ans=∑i=1n∑j=1m[gcd(i,j)=1]
直接莫反:
∑
i
=
1
n
∑
j
=
1
m
[
gcd
(
i
,
j
)
=
1
]
=
∑
i
=
1
n
∑
j
=
1
m
∑
d
∣
gcd
(
i
,
j
)
μ
(
d
)
=
∑
i
=
1
n
∑
j
=
1
m
∑
d
=
1
min
(
n
,
m
)
μ
(
d
)
[
d
∣
i
]
[
d
∣
j
]
=
∑
d
=
1
min
(
n
,
m
)
μ
(
d
)
∑
i
=
1
n
[
d
∣
i
]
∑
j
=
1
m
[
d
∣
j
]
=
∑
d
=
1
min
(
n
,
m
)
μ
(
d
)
⌊
n
d
⌋
⌊
m
d
⌋
\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]=\sum_{i=1}^n\sum_{j=1}^m\sum_{d|\gcd(i,j)}\mu(d)=\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^{\min(n,m)}\mu(d)[d|i][d|j]\\=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^n[d|i]\sum_{j=1}^m[d|j]=\sum_{d=1}^{\min(n,m)}\mu(d)\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor
i=1∑nj=1∑m[gcd(i,j)=1]=i=1∑nj=1∑md∣gcd(i,j)∑μ(d)=i=1∑nj=1∑md=1∑min(n,m)μ(d)[d∣i][d∣j]=d=1∑min(n,m)μ(d)i=1∑n[d∣i]j=1∑m[d∣j]=d=1∑min(n,m)μ(d)⌊dn⌋⌊dm⌋
那么我们就可以
O
(
n
)
O(n)
O(n) 求答案了,但还不够,考虑用整除分块以及前缀和优化,最终复杂度
O
(
n
)
O(\sqrt n)
O(n)
Code:
#include<bits/stdc++.h>
#define int long long
#define IOS ios::sync_with_stdio(false),cin.tie(NULL),cout.tie(NULL)
using namespace std;
const int N=2e6+1,M=2e6+1;
int t,n,m,k,ans=0;
struct fy{
int prv[N],cnt,mu[N],sum[N];
bool pr[N];
void ola(int x){
pr[1]=mu[1]=1;
for(int i=2;i<=x;i++){
if(!pr[i])
prv[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*prv[j]<=x;j++){
int u=i*prv[j];
pr[u]=1;
if(i%prv[j]==0){
mu[u]=0;
break;
}
else{
mu[u]=-mu[i];
}
}
}
for(int i=1;i<=x;i++)
sum[i]=sum[i-1]+mu[i];
}
}A;
signed main(){
IOS;
A.ola(N-1);
cin>>t;
while(t--){
cin>>n>>m>>k;
n/=k,m/=k;
ans=0;
for(int l=1,r;l<=min(n,m);l=r+1){
r=min(n/(n/l),m/(m/l));//
ans+=(A.sum[r]-A.sum[l-1])*(n/l)*(m/l);
}
cout<<ans<<"\n";
}
return 0;
}
注意一些细节。