一.线性筛基础基础模型
我们可以根据代码来分析:
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
//具体操作
}
for(int j=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
//具体操作
break;
}
//具体操作
}
}
核心原理:
线性筛的基本思想是用每个数的最小素因子把它筛掉。(等价于用最大的因子筛掉这个数)因为每个数的最大因子(或者说是最小素因子)是唯一的,所以说只会被筛一次,所以说是
O(n)
O
(
n
)
的。
在线性筛中,比较神奇的是当i%prime[j]=0的时候会break掉,但是其实i*prime[j+1]这个数并没有被筛掉(因为prime[j+1]并不是i的最小素因子)。而是要等到枚举到
i′=iprime[j]∗prime[j+1]
i
′
=
i
p
r
i
m
e
[
j
]
∗
p
r
i
m
e
[
j
+
1
]
,用
i′∗priem[j]
i
′
∗
p
r
i
e
m
[
j
]
筛掉。
二.线性筛的应用
运用的前提:
因为我们可以发现,在之前的板当中,一共有三处我们可以进行具体的操作。
- 当x是一个质数时
- 当x的最小的素因子只有一个时,也就是x等于两个互质的数相乘的时候。
- 当x的最小素因子不止一个时,也就是x等于两个不互质且gcd=那个素数的数相乘的之后。
然后我们就可以根据以上三种情况来对我们需要的函数进行分类讨论,从而做到线性求出一些函数的值。
1.求欧拉函数值
欧拉函数的性质:
1. 当(i,j)=1的时候,
ϕ(i∗j)=ϕ(i)∗ϕ(j)
ϕ
(
i
∗
j
)
=
ϕ
(
i
)
∗
ϕ
(
j
)
2. 对于任意的质数,
ϕ(i)=i−1
ϕ
(
i
)
=
i
−
1
3. 对于任意的质数,
ϕ(pk)=(p−1)∗pk−1
ϕ
(
p
k
)
=
(
p
−
1
)
∗
p
k
−
1
根据之前总结的结论有:
当x为质数时,已经有结论了;
当x只有一个最小素因子时:
ϕ(i×p)=ϕ(i)∗ϕ(p)=ϕ(i)∗(p−1)
ϕ
(
i
×
p
)
=
ϕ
(
i
)
∗
ϕ
(
p
)
=
ϕ
(
i
)
∗
(
p
−
1
)
当x有多个最小素因子时:
ϕ(i×p)=ϕ(ipk)∗ϕ(pk+1)
ϕ
(
i
×
p
)
=
ϕ
(
i
p
k
)
∗
ϕ
(
p
k
+
1
)
=ϕ(ipk)∗pk∗(p−1)
=
ϕ
(
i
p
k
)
∗
p
k
∗
(
p
−
1
)
=ϕ(ipk)∗ϕ(pk)∗p
=
ϕ
(
i
p
k
)
∗
ϕ
(
p
k
)
∗
p
=ϕ(i)∗p
=
ϕ
(
i
)
∗
p
然后就可以筛了:
phi[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
phi[i]=i-1;
}
for(intj=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
2.求莫比乌斯函数值
求莫比乌斯函数比较简单。
当x为质数时:
μ(x)=−1
μ
(
x
)
=
−
1
当x只有一个素因子时:
μ(i∗p)=μ(i)∗μ(p)=−μ(i)
μ
(
i
∗
p
)
=
μ
(
i
)
∗
μ
(
p
)
=
−
μ
(
i
)
证明:
若i有不止一个素因子,则显然成立。
否则有:
μ(i∗p)=(−1)n+m=μ(n)∗μ(m) μ ( i ∗ p ) = ( − 1 ) n + m = μ ( n ) ∗ μ ( m )
当x有不止一个素因子时:
μ(i∗p)=0
μ
(
i
∗
p
)
=
0
(由莫比乌斯函数定义可知)
那么就可以筛了:
miu[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
miu[i]=-1;
}
for(intj=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
miu[i*prime[j]]=0;
break;
}
miu[i*prime[j]]=-miu[i];
}
}
3.求逆元
逆元是具有完全积性的:
inv(x)∗x≡1(modMO) i n v ( x ) ∗ x ≡ 1 ( m o d M O )
inv(y)∗y≡1(modMO) i n v ( y ) ∗ y ≡ 1 ( m o d M O )
⇒inv(x)∗inv(y)∗x∗y≡1(modMO) ⇒ i n v ( x ) ∗ i n v ( y ) ∗ x ∗ y ≡ 1 ( m o d M O )
⇒inv(x∗y)=inv(x)∗inv(y) ⇒ i n v ( x ∗ y ) = i n v ( x ) ∗ i n v ( y )
这样就可以讨论了:
当x为奇数时,由费马小定理有: inv[x]=PowMod(x,MO−2) i n v [ x ] = P o w M o d ( x , M O − 2 )
因为逆元具有完全积性,那么其他情况就是: inv[x∗y]=inv[x]∗inv[y] i n v [ x ∗ y ] = i n v [ x ] ∗ i n v [ y ]
inv[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
inv[i]=PowMod(i,MO-2);
}
for(intj=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
inv[i*prime[j]]=inv[i]*inv[prime[j]];
if(i%prime[j]==0)
break;
}
}
其实还有一种求inv的方式,也是线性的,且代码简洁:
for(int i=1;i<=MAXN;i++)
inv[i]=(MO-MO/i)*inv[MO%i]%MO;
证明(网上大佬的证明(略有改动)):
设t=MO/i,k=MO%i,那么有:
t∗i+k≡0(modMO) t ∗ i + k ≡ 0 ( m o d M O )
⇒−t∗i≡k(modMO) ⇒ − t ∗ i ≡ k ( m o d M O )
两边同除i*k
−t∗inv(k)≡inv(i)(modMO) − t ∗ i n v ( k ) ≡ i n v ( i ) ( m o d M O )
⇒(MO−MO/i)∗inv(k)≡inv(i)(modMO) ⇒ ( M O − M O / i ) ∗ i n v ( k ) ≡ i n v ( i ) ( m o d M O )
快速求单个数的逆元:
设m,n互质,求inv(m)在模n的意义下的逆元。
有欧拉定理可得:
mϕ(n)≡1(modn)
m
ϕ
(
n
)
≡
1
(
m
o
d
n
)
mϕ(n)−1≡m−1(modn)
m
ϕ
(
n
)
−
1
≡
m
−
1
(
m
o
d
n
)
通过通项公式求出phi(n)后就可以用PowMod求出逆元了。
4.求正因子数目
设x的正因子数目为d(x)。
当x为质数时:d(x)=2;
当x只有一个最小素因子时:
设 x=∏paii x = ∏ p i a i y=∏qbii y = ∏ q i b i ,且gcd(x,y)=1。
那么有 d(x∗y)=∏paii∗∏qbii=∏(ai+1)∗∏(bi+1)=d(x)∗d(y) d ( x ∗ y ) = ∏ p i a i ∗ ∏ q i b i = ∏ ( a i + 1 ) ∗ ∏ ( b i + 1 ) = d ( x ) ∗ d ( y )
当x有不止一个素因子时:
假设e(x)为x的最小素因子的个数,即如果最小素因子为n的话,e(x)就是满足
ne(x)|x
n
e
(
x
)
|
x
的最大值。
那么有
d(i∗prime[j])=d(i)e(i)+1∗(e(i)+2)
d
(
i
∗
p
r
i
m
e
[
j
]
)
=
d
(
i
)
e
(
i
)
+
1
∗
(
e
(
i
)
+
2
)
。
先从d(i)中除掉e(i)+1,变成了d(i/n),然后又因为e(i)会加1,就需要再乘上e(i)+2,变成了d(i*prime[j]);
那么如何来求e(x)呢?
当x为质数时,e(x)=1;
当x只有一个最小素因子时,e(x)=1;
当x有不止一个最小素因子时,e(i*prime[j])=e[i]+1;
这样就可以做了。
d[1]=1,e[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
d[i]=2;
e[i]=1;
}
for(int j=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
d[i*prime[j]]=d[i]/(e[i]+1)*(e[i]+2);
e[i*prime[j]]=e[i]+1;
break;
}
e[i*prime[j]]=1;
d[i*prime[j]]=d[i]*2;
}
}
5.求正因子之和
设x的正因子之和为s(x)。
我们可以模仿上一道题的思路来想。其实,证明(x,y)=1时s(x*y)=s(x)*s(y)在这里是没有意义的,因为我们涉及到的是一个合数乘上一个质数,如果没有运用到这个性质,那就浪费掉了。
我们设x的最小素因子为n,那么令
f(x)=∑e(x)j=0nj
f
(
x
)
=
∑
j
=
0
e
(
x
)
n
j
,那么我们就可以来讨论了。
至于为什么这么定义,是因为如果
x=∏paii
x
=
∏
p
i
a
i
,那么
s(x)=∏i(∑aij=1pji)
s
(
x
)
=
∏
i
(
∑
j
=
1
a
i
p
i
j
)
,具体证明需要用到组合数学或者是多项式乘法:
就是从左到右像红色的路径这样的单条路径权值积的和。
那么就可以来讨论了。
当x为质数时:s(x)=x+1,f(x)=x+1;
当x只有一个素因子时:
s(i*prime[j])=s(i)+s(i)*prime[j];//选prime[j]或不选
f(i*prime[j])=prime[j]+1;
当x有不止一个素因子时:
s(i*prime[j])=s(i)/f[i]*(f[i]*prime[j]+1);
f(i*prime[j])=f(i)*prime[j]+1;//有点像是二进制的感觉
s[1]=1,f[1]=1;
for(int i=2;i<=MAXN;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
s[i]=i+1;
f[i]=i+1;
}
for(int j=0;prime[j]*i<=MAXN;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0)
{
s[i*prime[j]]=s[i]/f[i]*(f[i]*prime[j]+1);
f[i*prime[j]]=f[i]*prime[j]+1;
break;
}
f[i*prime[j]]=prime[j]+1;
s[i*prime[j]]=s[i]*prime[j]+s[i];
}
}
就是这样的啦(〃’▽’〃)