莫比乌斯反演
前置芝士:数论分块
求 n∑i=1⌊ni⌋,其中 n≤1012。
可以发现,⌊ni⌋ 最多只有 √n 种取值,所以只需要枚举每种取值对应 i 的取值范围即可。
ll ans = 0;
for(int i = 1,j;i <= n;i = j+1)
{
j = n/(n/i);
ans += (n/i)*(j-i+1);
}
积性函数:
如果函数 f 满足 ∀a,b,gcd(a,b)=1,f(ab)=f(a)f(b),则 f 为积性函数。
- 欧拉函数:φ(n),即 1 到 n 中与 n 互质的个数。
- 莫比乌斯函数:μ(i)=⎧⎨⎩1n=1(−1)kn=p1p2…pk0otherwise
- 因子函数:d(n),即 n 的因子个数。
- 除数函数:σ(n)=∑d∣nd,即 x 的因子之和。
完全积性函数:
如果函数 f(n) 满足 ∀a,b,f(ab)=f(a)f(b),则 f(n) 为完全积性函数。
- 常数函数:I(n)=1
- 恒等函数:Id(n)=n
- 单位函数:ε(n)=[n=1]
狄利克雷卷积:
(f∗g)(n)=∑d∣nf(d)g(nd)
性质:
-
交换律:f∗g=g∗f
-
结合律:(f∗g)∗h=f∗(g∗h)
-
分配律:(f+g)∗h=f∗h+f∗h
-
如果 f,g 是积性函数,则 f∗g 也是积性函数。比如:
- Id∗I=σ
- I∗I=d
- φ∗I=Id
- μ∗I=ε
下面是根据上面推出来的:
-
ε∗f=f,即 ε 与任何函数进行狄利克雷卷积后都是函数本身;
-
φ∗d=φ∗I∗I=Id∗I=σ
-
∵ε∗Id=Id∴μ∗I∗Id=φ∗I∴μ∗Id=φ
-
μ∗σ=μ∗Id∗I=ε∗Id=Id
莫比乌斯反演:[n=1]=∑d∣nμ(d)
其实莫比乌斯反演还有一种形式:g(n)=∑d∣nf(d)↔f(n)=∑d∣nμ(d)d(nd),也可以写成:f∗I=g↔f=g∗μ。
证明:μ∗I=ε
当 n=1 时显然成立,当 n>1 时,设 n=pc11pc22…pckk,由于有平方因子的数的 μ 为 0,所以只需要证 n=p1p2…pk 时 ∑d∣nμ(nd)=0成立即可。
μ∗I=∑d∣nμ(nd)=μ(1)+μ(p1)+…+μ(pk)+μ(p1p2)+μ(p1p3)+…+μ(pk−1pk)+…+μ(p1p2…pk)=k∑i=0(−1)i(ki)=0(1)(2)(3)(4)
欧拉反演:n=∑d∣nφ(d)
证明:φ∗I=Id
设 n=pc11pc22…pckk,因为 φ 是积性函数,则只需要证 n′=pc 时成立即可。
φ∗1=∑d∣nφ(nd)=c∑i=0φ(pi)=1+p0×(p−1)+p1×(p−1)+…+pc−1×(p−1)=pc=Id(5)(6)(7)(8)(9)
线性筛 φ 和 μ
线性筛分有重复因子和没重复因子两种情况,直接分情况讨论即可。
int p[N],phi[N],mu[N],cnt;
bool isp[N];
ll sphi[N],smu[N];
void init()
{
phi[1] = mu[1] = 1;
for(int i = 2;i < N;i++)
{
if(!isp[i]){p[++cnt] = i;phi[i] = i-1;mu[i] = -1;}
for(int j = 1;j <= cnt&&p[j]*i < N;j++)
{
int now = i*p[j];
vis[now] = 1;
if(i%p[j]==0)
{
phi[now] = phi[i]*p[j];mu[now] = 0;
break;
}
phi[now] = phi[i]*(p[j]-1);mu[now] = -mu[i];
}
}
for(int i = 1;i < N;i++)
sphi[i] = sphi[i-1]+phi[i],smu[i] = smu[i-1]+mu[i];
}
例题
-
最基本的应用
求 n∑i=1m∑j=1[gcd(i,j)=1] 和 n∑i=1m∑j=1gcd(i,j),T 组数据,n,m≤2×106,T≤1000
题太多了,甚至有十倍经验。
直接莫比乌斯反演(以下默认 n≤m):
n∑i=1m∑j=1[gcd(i,j)=1]=n∑i=1m∑j=1∑d∣i,d∣jμ(d)=n∑d=1μ(d)⌊nd⌋∑i=1⌊md⌋∑j=11=n∑d=1μ(d)⌊nd⌋⌊md⌋(10)(11)(12)
n∑i=1m∑j=1gcd(i,j)=n∑i=1m∑j=1∑d∣i,d∣jφ(d)=n∑d=1φ(d)⌊nd⌋∑i=1⌊md⌋∑j=11=n∑d=1φ(d)⌊nd⌋⌊md⌋(13)(14)(15)
发现式子中只有 ⌊nd⌋ 和 ⌊md⌋,直接数论分块即可,时间复杂度 O(n+T√n)。
ll ans1 = 0,ans2 = 0;
for(int i = 1,j;i <= n;i = j+1)
{
j = min(n/(n/i),m/(m/i));
ans1 += (smu[j]-sum[i-1])*(n/i)*(m/i);
ans2 += (sphi[j]-sphi[i-1])*(n/i)*(m/i);
}
总结:莫比乌斯反演最重要的就是处理和式,经常推不出式子了就尝试一下交换和式
- P2257 YY的GCD
求 n∑i=1m∑j=1[gcd(i,j)∈prime], T 组数据,n,m≤107,T≤104。
考虑枚举 gcd(i,j):
n∑i=1m∑j=1[gcd(i,j)∈prime]=∑p∈primen∑i=1m∑j=1[gcd(i,j)=p]=∑p∈prime⌊np⌋∑i=1⌊mp⌋∑j=1[gcd(i,j)=1]=∑p∈prime⌊np⌋∑d=1μ(d)⌊npx⌋⌊mpx⌋(16)(17)(18)
枚举 T=px:
=n∑T=1⌊nT⌋⌊mT⌋∑p∣T,p∈primeμ(⌊Tp⌋)
令 f(n)=∑p∣n,p∈primeμ(⌊np⌋),只需要预处理出 f(n) 的前缀和即可。具体的,枚举每一个质数,然后用类似埃式筛的方法求出 f(n)。时间复杂度 O(nloglogn+T√n)。
for(int i = 1;i <= cnt;i++)
for(int j = p[i];j < N;j += p[i])
f[j] += mu[j/p[i]];
for(int i = 1;i < N;i++)f[i] += f[i-1];
for(int i = 1,j;i <= min(n,m);i = j+1)
{
j = min(n/(n/i),m/(m/i));
ans += (f[j]-f[i-1])*(n/i)*(m/i);
}
-
设 d(n) 为 n 的约数个数,求 n∑i=1m∑j=1d(ij),T 组数据,T,n,m≤5000。
考虑转化 d(ij),将 ij 的因子与 i 和 j 的因子一一对应。如果 ij 的因子 k 中有一个因子 pc,i 中有 pa,j 中有 pb,则 c≤a+b。 那么:
- 如果 c≤a,那么在 i 中选择 pa;
- 如果 c>a,那么在 j 中选择 pc−a。
所以如果在 i 中选择一个因子 x,在 j 中选择一个因子 y 且 gcd(x,y)=1,则 x,y 一定对应一个 ij 的因子。所以 d(ij)=∑x∣i∑y∣j[gcd(x,y)=1]
n∑i=1m∑j=1d(ij)=n∑i=1m∑j=1∑x∣i∑y∣j[gcd(x,y)=1]=n∑x=1m∑y=1⌊nx⌋⌊my⌋[gcd(x,y)=1]=n∑x=1m∑y=1⌊nx⌋⌊my⌋∑d∣x,d∣yμ(i)=n∑d=1μ(i)⌊nd⌋∑x=1⌊md⌋∑x=1⌊nxd⌋⌊myd⌋=n∑d=1μ(i)(⌊nd⌋∑x=1⌊nxd⌋)(⌊md⌋∑y=1⌊myd⌋)(19)(20)(21)(22)(23)
发现括号里面的东西可以预处理,令 f(n)=n∑i=1⌊ni⌋,则原式等于 n∑d=1μ(i)f(⌊nd⌋)f(⌊md⌋),数论分块即可。时间复杂度 O(n√n+T√n)。
for(int i = 1;i < N;i++)
for(int j = 1,k;j <= i;j = k+1)
{
k = i/(i/j);
f[i] += (k-j+1)*(i/j);
}
for(int i = 1,j;i <= min(n,m);i = j+1)
{
j = min(n/(n/i),m/(m/i));
ans += (smu[j]-smu[i-1])*f[n/i]*f[m/i];
}
-
求:n∑i=1m∑j=1lcm(i,j),T 组数据,n,m≤107,T≤104
因为 lcm(i,j)=ijgcd(i,j)=gcd(i,j)×igcd(i,j)×jgcd(i,j),枚举 gcd(i,j):
n∑i=1m∑j=1lcm(i,j)=n∑d=1d×⌊nd⌋∑i=1⌊md⌋∑j=1[gcd(i,j)=1]×ij
为了单独计算后半边式子,记 :
sum(n,m)=n∑d=1d×n∑i=1m∑j=1[gcd(i,j)=1]×ij
另外,记 S(n)=n∑i=1i=n(n+1)2,那么:
sum(n,m)=n∑i=1m∑j=1ij×∑d∣i,d∣jμ(d)=n∑d=1μ(d)×d2×⌊nd⌋∑i=1⌊md⌋∑j=1ij=n∑d=1μ(d)d2×S(⌊nd⌋)S(⌊md⌋)(24)(25)(26)
其实到这里就可以数论分块套数论分块做了,时间复杂度 O(Tn34),但事实上原式还可以优化。
n∑d=1d×sum(⌊nd⌋,⌊md⌋)=n∑d=1d×⌊nd⌋∑k=1μ(k)k2×S(⌊ndk⌋)S(⌊mdk⌋)
把 d 乘进去:
n∑d=1⌊nd⌋∑k=1μ(k)k×dk×S(⌊ndk⌋)S(⌊mdk⌋)
先枚举 T=dk:
n∑T=1S(⌊nT⌋)S(⌊mT⌋)×T∑k∣Tμ(k)k
发现后半部分其实是可以预处理的,只需要求出 T∑k∣Tμ(k)k 的前缀和即可。令函数 f(n)=∑k∣nμ(k)k,不难证明 f 是积性函数,所以可以在线性筛的时候顺便求。具体的,如果在用 i 筛 i×p 时 x 有平方因子,即 p∣i,因为 μ(n) 在 n 有平方因子时为 0,所以 f(i×p)=f(i);如果没有平方银子,根据积性函数的性质,有 f(i×p)=f(i)×f(p)。时间复杂度 O(T√n)。
void init()
{
f[1] = 1;
for(int i = 2;i < N;i++)
{
if(!vis[i])p[++cnt] = i,f[i] = mod+1-i;
for(int j = 1;j <= cnt&&i*p[j] < N;j++)
{
int now = i*p[j];vis[now] = 1;
if(i%p[j]==0){f[now] = f[i];break;}
f[now] = f[i]*f[p[j]]%mod;
}
}
for(int i = 1;i < N;i++)f[i] = (f[i-1]+f[i]*i)%mod;
}
for(int i = 1,j;i <= n;i = j+1)
{
j = min(n/(n/i),m/(m/i));
(ans += (f[j]-f[i-1]+mod)*S(n/i)%mod*S(m/i)) %= mod;
}
-
给定 n,m,a,求 n∑i=1m∑j=1σ(gcd(i,j))[σ(gcd(i,j))≤a],T 组数据,n,m≤105,T≤2×104。
先考虑没有 a 的限制:
ans=n∑i=1m∑j=1σ(gcd(i,j))=n∑d=1σ(d)⌊nd⌋∑i=1⌊md⌋∑j=1[gcd(i,j)=1]=n∑d=1σ(d)⌊nd⌋∑i=1μ(i)⌊ndi⌋⌊mdi⌋(27)(28)(29)
枚举 T=di:
ans=n∑T=1⌊nT⌋⌊mT⌋∑d∣Tσ(d)μ(⌊Td⌋)(30)
令 f(x)=∑d∣xσ(d)μ(⌊xd⌋),此时只有当 σ(d)≤a 时,才会对 f(x) 产生贡献。
于是可以按 a 从小到大对询问排序,每次暴力加入所有 σ(d)≤a 的 d,并更新 f(x)。由于求答案还需要知道 f 的前缀和,所以用树状数组维护即可。每个数插入的次数为调和级数,而插入一次时间复杂度为 O(logn),所以总时间复杂度就是 O(nlog2n+T√nlogn)。
杜教筛
介绍:
杜教筛可以在 O(n23) 的时间复杂度中求出一类积性函数的前缀和。假设 f 是一个积性函数,如果能找到另一个积性函数 g 使得 g 和f∗g 的前缀和都能快速求出,那么就能用杜教筛求出 f 的前缀和。
比如:
- 可以在 O(1) 的时间内求出 1 和 ε 的前缀和,而 μ∗I=ε,所以可以用杜教筛求出 μ 的前缀和。
- 可以在 O(1) 的时间内求出 1 和 Id 的前缀和,而 φ∗I=Id,所以可以用杜教筛求出 φ 的前缀和。
杜教筛:
现在要求积性函数 f 的前缀和,令 S(n)=n∑i=1f(i)。
再找一个积性函数 g,则 f∗g 的前缀和为:
n∑i=1(f∗g)(i)=n∑i=1∑d∣if(d)g(id)=n∑d=1g(d)⌊nd⌋∑i=1f(i)=n∑d=1g(d)S(⌊nd⌋)(31)(32)(33)
接着考虑 g(1)S(n) 等于什么,发现:
g(1)S(n)=n∑i=1g(i)S(⌊ni⌋)−n∑i=2g(i)S(⌊ni⌋)=n∑i=1(f∗g)(i)−n∑i=2g(i)S(⌊ni⌋)(34)(35)
此时只需要找一个积性函数 g 使得 g 和 f∗g 的前缀和都可以快速求出,就可以数论分块递归来求了。
伪代码:
ll sum_g(ll n);
ll sum_fg(ll n);
ll sum_f(ll n)
{
ll ans = sum_fg(n);
for(ll i = 2,j;i <= n;i = j+1)
{
j = n/(n/i);
ans -= (sum_g(j)-sum_g(i-1))*sum_f(n/i);
}
return ans;
}
杜教筛是数论分块里面递归,时间复杂度可以看作 O(n34),具体咋证的可以看以下式子感性理解:
√n∑i=1O(√i)+O(√ni)=O(n34)
其实可以预处理出前 m 个答案,然后再用杜教筛,时间复杂度就是:
⌊nm⌋∑i=1O(√ni)=O(n√m)
一般为了平均,当 m=n23 时,时间复杂度也是 O(n23)。
μ,φ 的前缀和
- μ∗I=ε,而 I(n) 的前缀和就是 n,ε(n) 的前缀和就是 1。
- φ∗I=Id,而 Id(n) 的前缀和就是 n(n+1)2。
代码中 smu,sphi 是提前筛好的 μ 和 φ 的前缀和,Smu,Sphi 是记忆化。
ll sum_mu(int n)
{
if(n < N)return smu[n];
if(Smu[n])return Smu[n];
ll ans = 1;
for(ll i = 2,j;i <= n;i = j+1)
{
j = n/(n/i);
ans -= (j-i+1)*sum_mu(n/i);
}
return Smu[n] = ans;
}
ll sum_phi(int n)
{
if(n < N)return sphi[n];
if(Sphi[n])return Sphi[n];
ll ans = n*(n+1ll)/2;
for(ll i = 2,j;i <= n;i = j+1)
{
j = n/(n/i);
ans -= (j-i+1)*sum_phi(n/i);
}
return Sphi[n] = ans;
}
扩展:
求 f(n)=φ(i)×i 的前缀和。
令 g=Id,则:
(f∗g)(n)=∑d∣n(φ(d)×d)×nd=n∑d∣nφ(d)=n2(36)(37)(38)
所以 f∗g 的前缀和就是 n(n+1)(2n+1)6。
PN 筛
咕咕咕
Min25 筛
咕咕咕
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!