线性筛法
//线性筛法求质数。
memset(mark,0,sizeof(mark));
pt=0;
for(int i=2;i<=N;i++)
{
if(!mark[i]){
prime[pt++]=i;
mark[i]=i;
}
for(int j=0,u=dmin(mark[i],N/i);j<pt&&prime[j]<=u;j++)
mark[prime[j]*i]=prime[j];
}
marki
表示 i,除 1 以外的最小因子。
primei
维护质数序列。
保证 1~n 内的每一个合数被且只被除1外最小的因子筛过一次,对于当前处理到的 i 枚举小于
marki
的所有质数,将后面的数筛过一遍。时间复杂度
O(n)
。
证明正确性:每一个合数被且只被除1外最小的因子筛过一次。
设合数 P=p1^k1
×
p2^k2
×
p3^k3
×
…
×
pn^kn
其中
p1,p2,p3...pn
为质数,且
p1<p2<p3...<pn
;
k1,k2,k3...kn
为正整数。
若P不是被 P’=p1^(k1-1)
×
p2^k2
×
p3^k3
×
…
×
pn^kn 经过 p1 筛到,
则任意一个除去一个其他因子
pj
得到的数都只能枚举到
p1
。由已知条件,
pj>p1
,则无法筛到P。
积性函数
1.定义。若
f(n)
是积性函数,则当
gcd(a,b)=1时,f(a×b)=f(a)×f(b)
。
若
f(a×b)=f(a)×f(b)
,无限制条件,则
f(n)
被成为完全积性函数。
2.性质。
1)
gcd(a,b)=1时,f(a×b)=f(a)×f(b)
2)
f(1)=1
欧拉函数
1.定义:
其中p1, p2……pn为x的所有质因数(只出现一次),x是不为0的整数。
2.数学意义: φ(n) 表示表示 [1,n] 中n 的互质数个数。
3.性质。
1)φ(n)为积性函数,非完全积性。
2)欧拉定理:若
gcd(a,p)=1
,则
aφ(p)≡1(modp)
。
⟹
费马小定理:若
gcd(a,p)=1
,且p是质数,则
ap−1≡1(modp)
。
3) ∑d|nφ(d)=n 神犇的详解:法里级数展开证明
4)线性筛法原理:
φ(n)=∏ipki−1i(pi−1)
//线性筛法求欧拉函数。
memset(phi,0,sizeof(phi));
pt=0,phi[1]=1;
for(int i=2;i<=N;i++)
{
if(!phi[i]){
prime[pt++]=i;
phi[i]=i-1;
}
for(int j=0,u=N/i;j<pt&&prime[j]<=u;j++)
{
if(i%prime[j]==0)
{
phi[prime[j]*i]=phi[i]*prime[j];
break;
}
else phi[prime[j]*i]=phi[i]*(prime[j]-1);
}
}
莫比乌斯函数
神犇的详解
1.莫比乌斯反演:若
f(d)
是积性函数,则如下
g(n)
也是积性函数。
g(n)=∑d|nf(d)→f(n)=∑d|nμ(d)g(nd)
2.应用:通过 g(n) 的值推出 f(d) 的值。
3.莫比乌斯函数
μ(n)
。
1)函数式:
∑d|nμ(d)=[n=1]
2)求解
μ(m)
:由积性函数性质拆分为若干
μ(pk)
分别求解。有如下规律:
μ(1)=1,μ(p)=−1,μ(pk)=0(k>=2)
。
//线性筛法求莫比乌斯函数。
memset(miu,0,sizeof(miu));
memset(mark,0,sizeof(mark));
pt=0,miu[1]=1;
for(int i=2;i<=N;i++)
{
if(!mark[i]){
prime[pt++]=i;
miu[i]=-1;
}
for(int j=0,u=N/i;j<pt&&prime[j]<=u;j++)
{
mark[prime[j]*i]=1;
if(i%prime[j]==0)
{
miu[prime[j]*i]=0;
break;
}
else miu[prime[j]*i]=-miu[i];
}
}
注: marki 表示是否是质数。
乘法逆元
1.定义:如果
ax≡1(modp)
,且
gcd(a,p)=1
,则称
a
关于模
2.应用。:
目标:求解
ab(modp)
的值。
若 gcd(a,b)=1 ,则无法用直接利用模运算求得。
设
k=f(b)
(f(b)表示 b 在模 p 意义下的乘法逆元)
则
ab(modp)
等价于
a×k(modp)
可求解。
3.证明。
若
ax≡1(modp)
,且
gcd(a,p)=1
则
p|(ax−1)
等价于:
ax−1=py,y∈Z
写作
ax+py=1
不影响
x
解的结果。
通过exgcd求得x,y的整数解。
//扩展欧几里得算法求逆元。
void exgcd(int a,int b,ll &x,ll &y)
{
if(b==0)x=1,y=0;
else
{
exgcd(b,a%b,x,y);
ll tmp=x;
x=(y+mod)%mod;
y=(tmp-(a/b)*y%mod+mod)%mod;
}
}
若要求
exgcd的复杂度为
logn
,且有
nlnn≈nlogn
个质数。暴力exgcd就可以得出,但有更简单的方法。
神犇的证明
//线性筛法求逆元。
inv[1]=1;
for(int i=2;i<maxn;++i)
inv[i]=(p-p/i)*inv[p%i]%p;
终于都懂了!bravo。