一.积性函数
TIP. 其实这部分并不是太重要,只需要记得这些函数的定义和怎么用线性筛 O(n) 求解即可。
(1).定义
积性函数:对于函数
f(x),若
∀a,b∈N+,gcd(a,b)=1,f(ab)=f(a)f(b),则称
f(x) 为积性函数。
完全积性函数:对于函数
f(x),若
∀a,b∈N+,f(ab)=f(a)f(b),则称
f(x) 为完全积性函数。
- 常见的积性函数:φ,μ,σ,d
- 常见的完全积性函数:ε,I,id
其中:
ε(n)=[n=1],I(n)=1,id(n)=n
积性函数的性质:
- 设 n=∏piαi,那么 f(n)=∏f(piαi)
- 于是可以用线性筛求出 g(n)=p1α1,然后 f(n)=f(g(n))f(g(n)n),在 O(n) 时间内预处理积性函数的值。
- 若 f(n),g(n) 都是积性函数,那么 fg,gf 都是积性函数。
(2).欧拉函数 φ
1.定义
若
n=∏i=1spiki,其中
pi 为质数,则
φ(n)=n∏i=1spipi−1,表示小于等于
n 和
n 互质的数的个数$
2.性质:
- 若n为质数,φ(n)=n−1
- 若n为奇数,φ(n)=φ(2n)
- 若a⊥b,φ(ab)=φ(a)φ(b)
- n=∑d∣nφ(d)
- 若n=pk,且p为质数,φ(n)=pk−pk−1
3.求法
a.单个:
CPPint euler_phi(int n){
int ans=n,sq=sqrt(n);
for1(i,2,sq)
if(n%i==0){
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
b.多个:
设p1为n的最小质因子,n′=p1n,显然有φ(p1)=p1−1
若n′modp1=0,φ(n)=p1∗φ(n′)
若n′modp1=0,φ(n)=φ(p1)∗φ(n′)
CPPvoid euler_phi(int n){
phi[1]=1;
for1(i,2,n){
if(!vis[i]) pri.p_b(i),phi[i]=i-1;
for(int j: pri){
if(i>n/j) break;
vis[i*j]=1;
if(i%j==0){
phi[i*j]=phi[i]*j;
break;
}
phi[i*j]=phi[i]*phi[j];
}
}
}
(3).莫比乌斯函数 μ
1.定义
μ(n)=⎩⎨⎧10(−1)kn=1n的质因子最高次数>1n的质因子最高次数=1
2.性质
{10n=1n=1
即
d∣n∑μ(d)=ε(n),μ∗1=ε
3.求法
线性筛:
CPPvoid get_mu(int n){
mu[1]=1;
for1(i,2,n){
if(!vis[i]) pri.p_b(i),mu[i]=-1;
for(int j: pri){
if(i>n/j) break;
vis[i*j]=1;
if(i%j==0){
mu[i*j]=0;
break;
}
mu[i*j]=-mu[i];
}
}
}
(4).约数之和 σ 与 约数个数 d
1.定义
若
n=p1α1×p2α2×…pnαn
则:
d(n)=(1+α1)×(1+α2)×⋯×(1+αn)
σ(n)=(1+p11+p12+⋯+p1α1)×(1+p21+p22+⋯+p2α1)×⋯×(1+pn1+pn2+⋯+pnα1)
=i=1∏n(∑j=0αipij)
=i=1∏n1−pi1−piαi+1
2.求法
- 线性筛求 d
ti 表示
i 的最小质因子的次数,即
min(α1,…,αn)。
CPPvoid make_d(){
t[1]=d[1]=1;
for1(i,2,n){
if(!vis[i]) pri.p_b(i),t[i]=1,d[i]=2;
for(int j: pri){
if(i>n/j) break;
vis[i*j]=1;
if(i%j==0){
t[i*j]=t[i]+1;
d[i*j]=d[i]*(t[i*j]+1)/(t[i]+1);
break;
}
t[i*j]=1;
d[i*j]=d[i]*d[j];
}
}
}
- 线性筛求 σ
ti=min(∑j=0α1p1j,…,∑j=0αnpnj)。
CPPvoid make_sgm(){
sigma[1]=1;
for1(i,2,n){
if(!vis[i]) pri.p_b(i),t[i]=i+1,sigma[i]=i+1;
for(int j: pri){
if(i>n/j) break;
vis[i*j]=1;
if(i%j==0){
t[i*j]=t[i]*j+1;
sigma[i*j]=sigma[i]*t[i*j]/t[i];
break;
}
t[i*j]=j+1;
sigma[i*j]=sigma[i]*sigma[j];
}
}
}
二.数论分块
一般用来求形如
∑i=1nf(i)g(⌊in⌋)
若可以
O(1) 计算
f(r)−f(l) 时。
我们考虑将
⌊in⌋ 相同的数分块计算以使复杂度降至
O(n×poly(n))
其中的
poly(n) 是计算
g(i) 一次的复杂度。
最简单的例子:
∑i=1n⌊in⌋
证:按
⌊in⌋ 相同的数分块数是
n 量级的。
若
i≤n 则
⌊in⌋=⌊1n⌋…⌊nn⌋≥n 最多有
n 个取值。
若
i>n 则
⌊in⌋<n 最多有
n 个取值。
证毕。
所以数论整除可以将
O(n) 降为
O(n)
CPPLL H(LL n){
LL ans=0;
for(LL i=1,j;i<=n;i=j+1){
j=n/(n/i);
ans+=(j-i+1)*(n/i);
}
return ans;
}
三.狄利克雷卷积
(f∗g)(n)=d∣n∑f(d)g(dn)=a⋅b=n∑f(a)g(b)
- 满足:
- 交换律
- 结合律
- 分配率
- 单位元:f∗ε=f
- f∗g 也是积性函数
- 若 f∗g=ε,则称 g(x) 是 f(x) 的逆元,若 f(x) 为积性函数,则 g(x) 为积性函数
例子:
ϵ=μ∗1d=1∗1σ=id∗1φ=μ∗id⟺ϵ(n)=d∣n∑μ(d)⟺d(n)=d∣n∑1⟺σ(n)=d∣n∑d⟺φ(n)=d∣n∑d⋅μ(dn)
但是这么多函数之间进行狄利克雷卷积,很难记住结果怎么办?
有一种利用生成函数辅助的方法。
首先我们有
(f∗g)(pk)=k1+k2=k∑f(pk1)g(pk2)
定义生成函数
f~(x)=∑kf(pk)xk
则
f∗g~=f~⋅g~
首先积性函数只需考虑定义域为
pk 的值,所以以下函数定义域均为
pk
- μ~(x)=1−x
- I~(x)=1+x+x2+⋯=1−x1
- ε~(x)=1
- φ~(x)=1+(p−1)x+p(p−1)x2+p2(p−1)x3+⋯=1−px1−x
- id~(x)=1+px+p2x2+⋯=1−px1
这些函数之间的狄利克雷卷积的结果即为对应生成函数乘积所对应的函数了。
四.莫比乌斯变换
- f(n)=d∣n∑g(d)⟺g(n)=d∣n∑μ(d)f(dn)
- f(n)=d∣n∑g(d)⟺g(n)=n∣d∑μ(dn)f(d)
- 反演结论:
- d∣n∑μ(d)=[n=1]
- d∣n∑φ(d)=n
然后就是愉快的推式子环节了。
这块就先咕掉了。
因为主播都写在纸上了,没有时间转化成电子式子,并且用电脑推式子也很麻烦,有时间扫描成图片发一下吧,可能会放在别的地方。
五.杜教筛
1.介绍
找到一个积性函数
g,考虑其狄利克雷卷积的前缀和
i=1∑n(f∗g)(i)=i=1∑nd∣i∑f(d)g(di)=d=1∑ng(d)i=1∑⌊dn⌋f(i)=d=1∑ng(d)sf(⌊dn⌋)
于是就有:
g(1)sf(n)=i=1∑n(f∗g)(i)−i=2∑ng(i)sf(⌊in⌋)
这个公式称为杜教筛公式。
如果构造得当,那么
i=1∑n(f∗g)(i) 和
sg(n)=∑i=1ng(i) d都是可以
O(1) 求出的。
具体的,我们设置一个函数
GS(n) 求出
sf(n) 的值,然后递归调用即可。
时间复杂度:
O(n43)
CPPLL GS(int n){
LL ans=fg_sum(n);
for(LL l=2,r;l<=n;l=r+1){
r=(n/(n/1));
ans-=(g_sum(r)-g_sum(l-1))*GS(n\l);
}
return ans;
}
2.进一步优化
但这样可能优化的不是很多,所以我们再添加一些技巧来进一步优化。
先线性筛求出前
n32 个答案,之后再用杜教筛,并配合记忆化,使得
GS(i) 的每一个值都只计算一遍。
时间复杂度:
O(n32)
3.例题:求 μ 和 φ 的前缀和
注意到
μ~(x)=1−x,I~(x)=1+x+x2+⋯=1−x1,其乘积
=1=ε~(x)
所以令函数
I 为杜教筛公式中的函数
g。
则
i=1∑n(f∗g)(i)=i=1∑nε(i)=1,
sg(n)=∑i=1nI(i)=n
这样,我们就可以在
O(n32) 的时间复杂度求出
∑i=1nμ(i) 了。
同理,求
φ 也是注意到
φ~(x)=1+(p−1)x+p(p−1)x2+p2(p−1)x3+⋯=1−px1−x,I~(x)=1+x+x2+⋯=1−x1,其乘积
=1−px1=id~(x)
CPPint n,m;
bool vis[N];
vector<int> pri;
LL phi[N],mu[N];
void pre(int n){
phi[1]=mu[1]=1;
for1(i,2,n){
if(!vis[i]) pri.pb(i),phi[i]=i-1,mu[i]=-1;
for(int j: pri){
if(i>n/j) break;
vis[i*j]=1;
if(i%j==0){
phi[i*j]=phi[i]*j;
mu[i*j]=0;
break;
}
phi[i*j]=phi[i]*phi[j];
mu[i*j]=-mu[i];
}
}
for1(i,2,n) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
unordered_map<int,LL> ans_phi;
unordered_map<int,int> ans_mu;
LL GS_phi(LL n){
if(n<=(int)3e6) return phi[n];
if(ans_phi.count(n)) return ans_phi[n];
LL ans=1ll*n*(n+1)>>1ll;
for(LL l=2,r;l<=n;l=r+1){
r=(n/(n/l));
ans-=1ll*(r-l+1)*GS_phi(n/l);
}
return ans_phi[n]=ans;
}
LL GS_mu(LL n){
if(n<=(int)3e6) return mu[n];
if(ans_mu.count(n)) return ans_mu[n];
LL ans=1;
for(LL l=2,r;l<=n;l=r+1){
r=(n/(n/l));
ans-=1ll*(r-l+1)*GS_mu(n/l);
}
return ans_mu[n]=ans;
}
int x;
void solve(){
cin>>x;
cout<<GS_phi(x)<<" "<<GS_mu(x)<<"\n";
return ;
}
六、练习
好了,你已经学会莫反和杜教筛了,试着切掉以下这些水题吧。
求:
∑i=1n∑j=1m[gcd(i,j)=k]
f(a,b,k)=∑t=1min(a,b)μ(t)⌊tka⌋⌊tkb⌋
ans=f(b,d,k)−f(b,c−1)−f(a−1,c)+f(a−1,c−1)
ans(k)=∑t=1nμ(t)⌊tkn⌋⌊tkn⌋
求:
∑i=1n∑j=1mgcd(i,j)
f(a,b)=∑t=1nφ(t)⌊tn⌋⌊tn⌋
ans=2×f(a,b)−a×b
P2398 GCD SUM
ans(a,b)=∑t=1nφ(t)⌊tn⌋⌊tn⌋
求:
∑i=1n∑j=1mi×jgcd(i,j)
ans(n)=∑d=1nφ(d)d2sum3(⌊dn⌋)
求:
∑i=1n∑j=1mlcm(i,j)
ans(n,m)=∑T=1nT×sum1⌊Tn⌋×sum1⌊Tm⌋∑d∣Td×μ(d)
令
f(T)=∑d∣Td×μ(d)
f(p)=1−p,f(pk)=f(p)
ans(n,m)=∑T=1nT×f(T)×sum1⌊Tn⌋×sum1⌊Tm⌋
求:
∑i=1nlcm(i,n)
ans(n)=2n(∑d∣nd×φ(d)+1)
令
g(T)=∑d∣Td×φ(d)
g(p)=1+p×(p−1),g(pk)=g(pk−1)+p2k−1(p−1)
求:
∑i=1n∑j=1md(i×j)
ans(n,m)=∑d=1nμ(d)∑i=1⌊dn⌋⌊din⌋∑j=1⌊dm⌋⌊djm⌋
又有
∑i=1nd(i)=∑j=1n⌊jn⌋
∴ans(n,m)=∑d=1nμ(d)∑i=1⌊dn⌋d(i)∑j=1⌊dm⌋d(j)
求:
∏i=1n∏j=1mf(gcd(i,j)),f0=0,f1=1.fn=fn−1+fn−2,n≥2
ans=∏T=1n(∏d∣Tf(d)μ(⌊dT⌋))⌊Tn⌋⌊Tm⌋
内部的暴力预处理,外部数论分块。