一. 筛法
例题:codevs1430
1. 埃拉托斯特尼筛法
vis[1]=1;
for (int i=2;i<=n;i++)
if (!vis[i])
{
pri[++tot]=i;
for (int j=i*2;j<=n;j+=i)
vis[j]=1;
}
时间复杂度:O(nloglogn)
2. 欧拉筛法
写法:
vis[1]=1;
for (int i=2;i<=n;i++)
{
if (!vis[i])
pri[++tot]=i;
for (int j=1;j<=tot;j++)
{
if (i*pri[j]>n) break;
vis[i*pri[j]]=1;
if (i%pri[j]==0) break;
}
}
时间复杂度:O(n)
证:每个和数被最小的质因子筛去
二. 欧拉筛法解积性函数
1. 方法
步骤Ⅰ:证明积性
步骤Ⅱ:考虑下面三方面的实现:
①素数m,求f(m)
②m和比m的最小素因子小的素数n,求f(n*m)
③m和m的最小素因子n:求f(n*m)
2. 例子
(1)欧拉函数
题目:PC 1499
证明:设n=∏apii,m=∏bqii,且gcd(m,n)=1
∴ϕ(mn)=mn∏(1−1ai)∏(1−1bi)=[n∗∏(1−1ai)]∗[m∗(∏(1−1bi)]=ϕ(n)ϕ(m)
写法:
①素数m:ϕ(m)=m−1
②m和比m的最小素因子小的素数n:
ϕ(m∗n)=ϕ(m)∗phi(n)
③m和m的最小素因子n:ϕ(m∗n)=ϕ(m)∗n
phi[1]=1;
for (int i=2;i<=n;i++)
{
if (!phi[i])
{
pri[++tot]=i;
phi[i]=i-1;
}
for (int j=1;j<=tot;j++)
{
if (i*pri[j]>n) break;
if (i%pri[j]!=0)
phi[i*pri[j]]=phi[i]*phi[pri[j]];
else
{
phi[i*pri[j]]=phi[i]*pri[j];
break;
}
}
}
(2)莫比乌斯函数
题目:PC 1492
积性证明:
设n=∏apii,m=∏bqii,且gcd(m,n)=1
①若∃pi>1或qi>1,则μ(n∗m)=μ(n)∗μ(m)=0
②否则,μ(n∗m)=(−1)n+m=μ(n)∗μ(m)
综上,μ(n∗m)=μ(n)∗μ(m)
写法:
①素数m:μ(m)=−1
②m和比m的最小素因子小的素数n:
μ(m∗n)=μ(m)∗μ(n)
③m和m的最小素因子n:μ(n∗m)=0
mu[1]=vis[1]=1;
for (int i=2;i<=n;i++)
{
if (!vis[i])
{
mu[i]=-1;
pri[++tot]=i;
}
for (int j=1;j<=tot;j++)
{
if (i*pri[j]>n) break;
vis[i*pri[j]]=1;
if (i%pri[j]!=0)
mu[i*pri[j]]=mu[i]*mu[pri[j]];
else
{
mu[i*pri[j]]=0;
break;
}
}
}
(3) 乘法逆元inv(n,i)(n不变)
题目:PC 1494
积性证明:
∴(a∗b)∗(inv[a]∗inv[b])≡1(modn)
∴inv[a∗b]≡inv[a]∗inv[b]
写法:
①素数m:根据费马小定理,inv[m]≡Pow(m,n−2)
②m和比m的最小素因子小的素数n:inv[m∗n]=inv[m]∗inv[n]
③m和m的最小质因子n:
根据完全积性,inv[m∗n]=inv[m]∗inv[n]
注意,第①步是O(n)的。这是因为:
O(π(n)logn)=O(nlnnlogn)=O(nlognlogn)=O(n)。
vis[1]=inv[1]=1;
for (int i=2;i<n;i++)
{
if (!vis[i])
{
pri[++tot]=i;
inv[i]=Pow(i,n-2);
}
for (int j=1;j<=tot;j++)
{
if (i*pri[j]>n) break;
vis[i*pri[j]]=1;
inv[i*pri[j]]=inv[i]*inv[pri[j]];
if (i%pri[j]==0) break;
}
}
其实可以直接有一种递推的方法:
①当n=1时,inv[n]=1
②假设当前对于k,已经求出了inv[1],inv[2],…,inv[k-1],当前要求出inv[k]。
令a=nmodk,b=n/k
∴n=a+b∗k
∴a=n−b∗k
又∵inv[a]∗a≡inv[a]∗(n−b∗k)≡inv[a]∗−b∗k≡1
∴inv[k]=−b∗inv[a]
(4)最大公约数gcd(a,b)(b一定)
题目:PC 1495
证明:略
写法:求g(i)
①素数m:g(m)=gcd(m,b);
②m和比m的最小素因子小的素数n:
g(m∗n)=g(m)∗g(n);
③m和m的最小质因子n:
设ek(m)表示n的最大的幂,满足ek(m)|m
若(ek(m)∗n)|b,则g(m∗n)=g(m)∗n
若不满足,则g(m∗n)=g(m);
ek(i)不是积性函数,但也可以放在欧拉筛法里面求:
①素数m:ek(m)=m
②m和比m的最小素因子小的素数n:ek(m∗n)=n
③m和m的最小质因子n:ek(m∗n)=ek(m)∗n
(5)正因子数目d(n)
题目:PC 1496
证明:
设m=∏apii,n=∏bqii且m,n互质
∴d(mn)=d(∏apii∏bqii)=∏(pi−1)∏(qi−1)=d(m)∗d(n)
求法:求d(i)
①素数m:d(m)=2
②m和比m的最小素因子小的素数n:d(m∗n)=d(m)∗d(n)
③m和m的最小质因子n:
d(m∗n)=d(m)/(e(m)+1)∗(e(m∗m)+1),e(m)表示最大的满足ne(m)|m的值。
求e(i):
①素数m:e(m)=1
②m和比m的最小素因子小的素数n:e(m∗n)=1
③m和m的最小质因子n:e(m∗n)=e(m)+1
(6)正因子之和s(n)
题目:PC 1497
证明:
设m=∏apii,n=∏bqii且m,n互质,
则s(m∗n)=∏j(∑pij=0aji)∗∏j(∑qij=0bji)=s(m)s(n)
写法:
①素数m:s(m)=m+1
②m和比m的最小素因子小的素数n:s(m∗n)=s(m)∗s(n)
③m和最小质因数n:
s(m∗n)=s(∏apii∗ne(i)+1)=∏(∑pij=0aji)∗(∑e(i)+1j=0nj)=s(m)∗(∑e(i)+1j=0nj)(∑e(i)j=0nj)=s(m)∗ne(i)+2−1n−1∗n−1ne(i)+1−1=s(m)∗ne(i)+2−1ne(i)+1−1
记pe(m)=ne(i)+1,则可以在欧拉筛法的同时求出。
(8)另一些显而易见的积性函数
1(n):1(n)=1,完全积性
Id(n):Id(n)=n,完全积性
Idk(n):Idk(n)=n^k,完全积性
3. 实现技巧
(1)节省空间
很多时候,有些数组A可以当作另外一些数组B的功能拓展,这时候就可以把B省略掉。
例如,在求欧拉函数的时候,我们可以用phi来取代vis数组。因为没有求出来的phi(i)也就意味着i是素数。
又例如,在求莫比乌斯函数的时候,我们可以用mu来取代vis数组。理由同上。不过要把mu数组的初始值设为-1。
(2)模板
变量:
- 布尔数组vis[N]
- 素数表pri[N],tot
- 积性函数
模板:
void solve(void)
{
vis[1]=...=1; //赋初始值
for (int i=2;i<=n;i++)
{
if (!vis[i])
{
... //素数的积性函数值
pri[++tot]=i; //加入素数表
}
for (int j=1;j<=tot;j++)
{
if (i*pri[j]>n) break;
vis[i*pri[j]]=1;
... //公共部分
if (i%pri[j]!=0)
{
... //m和比m的最小素因子小的素数n的处理
}
else
{
... //m和m的最小素因子n的处理
break;
}
}
}
}
4. 变式
(1)“加性”函数
例题:PC 1498
有些涉及到素因子的函数,也可以使用欧拉筛法求解。很多时候可以辅助积性函数的求解,上面的例子中的e函数,
(2)A2
例题:XSY 1001
对于A2的积性函数,关键求出A的积性函数,然后顺便求解
(3)(A+1)(A−1)
例题:XSY 1001
通常一个乘积的形式,也可以通过互质的关系,转化为有关积性函数的问题。