莫比乌斯反演、杜教筛、Min_25筛学习笔记
前置知识
一、整除分块
即按照 \(\lfloor \frac{n}{i} \rfloor\) 的值域进行分块,块数 $ \leq \lfloor 2\sqrt n \rfloor $。分 $i \leq \lfloor \sqrt n \rfloor $ ,$ i > \lfloor \sqrt n \rfloor $ 讨论。
\(i\) 所在块的右端点为 $ \lfloor \frac{n}{ \lfloor \frac{n}{i} \rfloor } \rfloor $ ,代码即 $ r=n/(n/l) $
这里的证明:设所在块的值为 \(\frac{n}{l}\) ,则这个块内的 \(i\leq \frac{n}{k}\) ,把 \(k\) 带入即得。
这样,我们可以把 \(O(n)\) 的求 $ \sum _{i=1}^{n} \lfloor \frac{n}{i} \rfloor * f(i)$ 值过程变为 \(O(\sqrt n)\) ( \(f(i)\)用前缀和处理 )
- 无关的小知识
\(1^2+2^2+3^2+...+n^2=\frac{n(n+1)(2n+1)}{6}\)
\(1^3+2^3+3^3+...+n^3=\frac{n^2(n+1)^2}{4}\)
另外,在做题的时候时刻关注的是式子的形式和范围,除数不为0,r不大于n
二、普通生成函数
形如 \(f(x)=\sum _n a_nx^n\),此处a可以有限也可以无限
如序列 \(a=1,3,5,7...\) (编号从0开始) 的生成函数是 $ \sum _{n \geq 0} (2n+1)x^n$ ,如果a有通项公式,则它的普通生成函数的系数就是通项公式
基本运算
考虑 \(a,b\) 的普通生成函数 \(f(x),g(x)\)
加法,$ f(x) \pm g(x) =\sum _n (a_n \pm b_n)x^n$ ,即 $ f(x) \pm g(x) $ 是序列 $ q_n = (a_n \pm b_n) $ 的普通生成函数
乘法,或卷积,$ f(x)g(x)= \sum_n x^n \sum_{i=0}^n a_i b_{n-i} $,即 \(f(x)g(x)\) 是序列 \(q_n=\sum_{i=0}^n a_i b_{n-i}\) 的普通生成函数
关键在构造,一般指数是题中给的限制,某项系数是最终答案
可以解决多重集组合数
三、指数生成函数
形如 $f(x)=\sum_{n \geq 0} a_n \frac{x^n}{n!} $
这里,如果原数列是有限的,就把大于数列上界的 \(a_n\) 设为 0 即可
little question: 封闭形式如何得到?(坑)
加法同上
乘法(卷积),
\(f(x)g(x)=\sum_{i \geq 0} a_i \frac{x^i}{i!} \sum_{j \geq 0} b_j \frac{x^j}{j!}\)
\(=\sum_{n \geq 0} x^n \sum_{i=0}^n a_i b_{n-i} \frac{1}{i!(n-i)!}\)
$=\sum_{n \geq 0} \frac{x^n}{n!} \sum_{i=0}^{n} \frac{n!}{i!(n-i)!} a_i b_{n-i} $
\(=\sum_{n \geq 0} \frac{x^n}{n!} \sum_{i=0}^{n} C_n^i a_i b_{n-i}\)
即 \(f(x)g(x)\) 是序列 \(q_n=\sum_{i=0}^{n} C_n^i a_i b_{n-i}\)
可以解决多重集排列数
四、积性函数
一个数论函数,\(f(1)=1\),当\(gcd(a,b)=1\)时,有\(f(ab)=f(a)f(b)\),则\(f(n)\)为积性函数
ps.数论/算术函数指定义域为正整数、陪域为复数的函数(其实没啥用)
特别地,如果对于任意a,b(不一定互质),都有\(f(ab)=f(a)f(b)\),则\(f(n)\)为完全积性函数
欧拉函数和莫比乌斯函数都是积性函数
欧拉函数
\(\varphi(n)=\sum_{i=1}^n [gcd(i,n)=1]\)
又 \(\varphi(n)=n(1-\frac{1}{p_1})(1-\frac{1}{p_2})...(1-\frac{1}{p_s})\),其中p为n的质因数
即n以内,和n互质的数的个数
性质:\(\sum_{d|n} \varphi(d)=n\)
证明:将以n为分母,且小于等于1的分数写出来,化简后按分母归类,发现每一类 \(d\) 的个数都是 \(\varphi(d)\)
五、狄利克雷卷积
我们已经知道卷积的基本形式,类似的,我们定义两个数论函数 \(f,g\) 的狄利克雷卷积 \(t=f*g\) 满足:\(t(n)=\sum_{d|n}f(d)g(\frac{n}{d})\)
狄利克雷卷积的性质:
-
满足结合律 \((h*f)*g=h*(f*g)\)
-
满足交换律 \(f*g=g*f\)
-
满足分配率 \((f+g)*h=f*h+g*h\)
狄利克雷前后缀和
//后缀
for(int k=1;k<=cnt;k++){
if(pr[k]>tmp) break;
for(int x=tmp/pr[k]*pr[k];x>=pr[k];x-=pr[k]) add(t[x/pr[k]],t[x]);
}
//前缀
for(int k=1;k<=cnt;k++){
if(pr[k]>tmp) break;
for(int x=pr[k];x<=tmp;x+=pr[k]) add(t[x],t[x/pr[k]]);
}
正主出场:壹、莫比乌斯函数
定义:
\(\mu(n)=\left\{ \begin{array}{rcl} 1 & & n=1 \\ (-1)^s & & n=p_1p_2...p_s \\ 0 & & \text{n包含相同质因子}\\ \end{array} \right.\)
可以用线性筛O(n)求出
性质: $\sum_{d|n} \mu(d)=[n=1] $
简单证明:
\(n>1\) 时,\(n=p_1^{a_1}p_2^{a_2}..p_s^{a_s}\)
令 \(n'=p_1p_2...p_s\)
又 \(\sum_{d|n} \mu(d)=\sum_{d|n'}\mu(d)\)
约数由质因子的成绩构成,又有容斥原理
\(\sum_{d|n'}\mu(d)=C_s^0+(-1)C_s^1+(-1)^2C_s^2+...+(-1)^sC_s^s\)
\(=(1+(-1))^s\)
\(=0\)
与欧拉函数的联系:\(\sum_{d|n} \mu(d) \frac{n}{d} = \varphi(n)\)
证明是把n提出来,因式分解,化成欧拉函数的形式。
性质应用:\([gcd(i,j)=1]=\sum_{d|gcd(i,j)} \mu(d)\)


常用手法:
1.YY的GCD 经过和式变换后整除的分母有两个变量,直接另T等于其分母,更改枚举顺序即可
2.[SDOI2015]约数个数和 设d(x)为x的约数个数和,有推论\(d(ij)=\prod(t_i+1)=\prod(a_i+b_i+1)=\sum_{x|i}\sum_{y|j}[gcd(x,y)=1]\)
最后化成一个整除分块套整除分块的形式
3.我们看到莫比乌斯函数和欧拉函数都能处理gcd的问题,但如果这个式子里某一项就是一个完整而独立存在的gcd,不妨直接使用欧拉函数,更为方便
莫比乌斯反演
形式一
由 \(f(n)=\sum_{d|n}g(d)\)
推出 \(g(n)=\sum_{d|n}f(\frac{n}{d})\mu(d)\)
证明:令\(t(n)=\sum_{d|n}f(\frac{n}{d})*\mu(d)=\sum_{d|n}\mu(d)\sum_{i|\frac{n}{d}}g(i)=\sum_{i|n}g(i)\sum_{d|\frac{n}{i}}\mu(d)\)
当i=n时,\(\sum_{d|\frac{n}{i}}\mu(d)=1\),则\(t(n)=g(n)\)
当i取小于n的约数时,\(\sum_{d|\frac{n}{i}}\mu(d)=0\),则\(t(i)=0\)
相加得证。
形式二
由 \(f(n)=\sum_{n|d}g(d)\)
推出 \(g(n)=\sum_{n|d}\mu(\frac{d}{n})f(d)\)
又,有趣的事情在于,如果我们不学习莫比乌斯反演,只靠这之前的莫比乌斯函数性质+和式变换,大部分问题也是可以解决的。。。
(保守了,目前没见到无法解决的。。
总感觉每道莫反的题推法都十分类似。(当然我不是用莫反做的
\(for\) \(example:\) [国家集训队] Crash的数字表格 / JZPTAB
\(\sum_{i=1}^n\sum_{j=1}^mlcm(i,j)\)
\(=\sum_{i=1}^n\sum_{j=1}^m\frac{i*j}{gcd(i,j)}\)
\(=\sum_{i=1}^n\sum_{j=1}^m\sum_{d=1}^ni*j*\frac{1}{d}[gcd(i,j)=d]\)(单独枚举\(gcd\))
\(=\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}i*j*d[gcd(i,j)=1]\)(更改枚举范围,得到\(gcd=1\)的形式)
\(=\sum_{d=1}^n\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}i*j*d*\sum_{p=1}^n\mu(p)[p|i][p|j]\)(根据莫比乌斯函数推论)
\(=\sum_{d=1}^nd\sum_{p=1}^n\mu(p)\sum_{i=1}^{\lfloor \frac{n}{pd} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{pd} \rfloor}p^2*i*j\)(更改枚举范围+交换枚举顺序)
\(=\sum_{d=1}^nd\sum_{p=1}^n\mu(p)*p^2*\frac{(\lfloor \frac{n}{pd} \rfloor+1)*\lfloor \frac{n}{pd} \rfloor}{2}*\frac{(\lfloor \frac{m}{pd} \rfloor+1)*\lfloor \frac{m}{pd} \rfloor}{2}\)(根据题目要求,推出可进行整除分块的部分)
\(=\sum_{T=1}^n\frac{(\lfloor \frac{n}{T} \rfloor+1)*\lfloor \frac{n}{T} \rfloor}{2}*\frac{(\lfloor \frac{m}{T} \rfloor+1)*\lfloor \frac{m}{T} \rfloor}{2}\sum_{p=1}^n\mu(p)*p^2*\frac{T}{p}\)(分母有两个未知数,令\(T=\)分母,改为枚举\(T\),以便把整除的部分分离出来)
\(=\sum_{T=1}^n\frac{(\lfloor \frac{n}{T} \rfloor+1)*\lfloor \frac{n}{T} \rfloor}{2}*\frac{(\lfloor \frac{m}{T} \rfloor+1)*\lfloor \frac{m}{T} \rfloor}{2}\sum_{p=1}^n\mu(p)*pT\)(最后总会化成一个【整除分块*一个含\(\mu\)的函数】的形式)
好的,不得不承认,确实有些题直接用莫比乌斯反演更简单。
如 luogu P6271 一个人的数论。
考虑构造 \(f(n)=\sum_{i=1}^n i^k\) \(g(n)=\sum_{i=1}^n i^k [gcd(i,n)=1]\) ,求 \(g(n)\)
则 \(f(n)=\sum_{d|n} d^k g(\frac{n}{d})\) ,做莫比乌斯反演,有 \(g(n)=\sum_{d|n} \mu(d) d^k f(\frac{n}{d})\)
剩下是多项式转换内容。
贰、杜教筛
杜教筛可以在低于线性的时间复杂度内求出积性函数的前缀和。
常见的积性函数:\(\mu\),\(\varphi\),\(\sigma\)(约数和),\(d\)(约数个数)
注,无论数论函数f是否为积性函数,只要可以构造出恰当的数论函数g,便都可以考虑用杜教筛求f的前缀和
再来放一下这张图

其中“三个常用函数”\(\epsilon\),\(I\),\(id\) 均为完全积性函数
第三个"常用卷积关系"的推导:注意到\(\mu\) \(\varphi\)的卷积关系中都有\(I\),那么考虑把它们组合起来。
$ \varphi * I =id$ \(\to\) \(\varphi*I*\mu=id*\mu\) \(\to \varphi* \epsilon=id*\mu \to \varphi = \mu * id\)
算法思想:
考虑构造一个\(S(n)\)关于\(S(\lfloor \frac{n}{i} \rfloor)\)的递推式。
对于任意一个数论函数\(g\),必满足
\(\begin{aligned} \sum_{i=1}^{n}(f * g)(i) & =\sum_{i=1}^{n}\sum_{d \mid i}^ig(d)f(\frac{i}{d}) \\ & =\sum_{d=1}^ng(d)\sum_{d|i}^nf(\frac{i}{d})\text{(这一行被称为杜教筛变换)} \\ & =\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}f(i) \\ & =\sum_{d=1}^{n}g(d)S(\lfloor\frac{n}{d}\rfloor) \end{aligned}\)
其中 \((f*g)\) 为数论函数 \(f\) 和 \(g\) 的 狄利克雷卷积。
那么可以得到(杜教筛公式):
\(\begin{aligned} g(1)S(n) & = \sum_{i=1}^n g(i)S(\lfloor\frac{n}{i}\rfloor) - \sum_{i=2}^n g(i)S(\lfloor\frac{n}{i}\rfloor) \\ & = \sum_{i=1}^n (f * g)(i) - \sum_{i=2}^n g(i)S(\lfloor\frac{n}{i}\rfloor) \end{aligned}\)
假如我们可以构造恰当的数论函数 g 使得,
可以快速计算 \(\sum_{i=1}^nh(i)=\sum_{i=1}^n(f * g)(i)\);
可以快速计算 \(g\) 的前缀和,以用数论分块求解\(\sum_{i=2}^ng(i)S\left(\left\lfloor\dfrac{n}{i}\right\rfloor\right)\)。
则我们可以在较短时间内求得 \(g(1)S(n)\)。
Q:为什么我们要化成一个好求的函数h呢?直接求h的展开形式 \(\sum_{i=1}^n g(i)S(\lfloor\frac{n}{i}\rfloor)\) (它和 \(\sum_{i=2}^n g(i)S(\lfloor\frac{n}{i}\rfloor)\) 形式相同,只是范围不同)不行吗?
A:显然我们是由卷积的基本公式拆出来一个\(S(n)\),目的也是求解\(S(n)\). 如果此时我们再分别从 \(\sum_{i=1}^n g(i)S(\lfloor\frac{n}{i}\rfloor)\) 的形式去妄图\(O(\sqrt n)\)求解,那程序就会在\(i=1\)时陷入求\(S(n)\)的循环中无法自拔。(应该不会有人和我一样唐吧?
写法:发现该公式为\(S(n)\)的递归形式,在递归时加上记忆化,让每个\(S(i)\)只被算一次。
时间复杂度不会证明,如果线性预处理k个\(S(n)\),时间复杂度会得到优化。其中k取\(n^{\frac{2}{3}}\)时间复杂度最小值为\(O(n^\frac{2}{3}).\)
在推 \(g(x)\) 的式子时,注意观察到类似
\(\sum_{d|n}f(d)\) 的样子,总之就是观察+猜测;又,后面一项是从2开始枚举的,不妨推式子的时候直接写成 \(d<n\) 最后更改枚举范围即可。
模板
//杜教筛模版
#include<bits/stdc++.h>
#define ll long long
#define int long long
using namespace std;
const int k=2e6+5;
int T,n,vis[k],p[k],cnt;
ll phi[k],mu[k];
unordered_map<int,ll> mp1,mp2;
void init(){
mu[1]=1,phi[1]=1;
for(int i=2;i<k;i++){
if(!vis[i]) p[++cnt]=i,mu[i]=-1,phi[i]=i-1;
for(int j=1;j<=cnt&&i*p[j]<k;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];
break;
}
mu[i*p[j]]=-mu[i];
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
for(int i=2;i<k;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
ll gphi(int n){
if(n<k) return phi[n];
if(mp1.count(n)) return mp1[n];
ll ans=1ll*n*(n+1)/2;
for(int l=2,r;l<=n;l=r+1){
r=n/(n/l);
ans-=gphi(n/l)*(r-l+1);
}
return mp1[n]=ans;
}
ll gmu(int n){
if(n<k) return mu[n];
if(mp2.count(n)) return mp2[n];
ll ans=1;
for(int l=2,r;l<=n;l=r+1){//服了,这里+1会炸int
r=n/(n/l);
ans-=gmu(n/l)*(r-l+1);
}
return mp2[n]=ans;
}
signed main(){
scanf("%lld",&T);
init();
while(T--){
scanf("%lld",&n);
printf("%lld %lld\n",gphi(n),gmu(n));
}
return 0;
}
推式子方法:(以\(\sum_{i=1}^n\varphi(i^2)\)为例)
根据题目要求,得出\(f(i)=\varphi(i^2)=\varphi(i)*i\)
则\(S(n)=\sum_{i=1}^nf(i)\)
此时我们需要构造一个\(g(x)\)使得\(h(i)=(f*g)(i)\)的前缀和好求
写出形式,带入\(f(x)\)
\(h(i)=(f*g)(i)\)
\(=\sum_{d|i}f(d)g(\frac{i}{d})\)
\(=\sum_{d|i}\varphi(d)dg(\frac{i}{d})\)
此时又有欧拉函数的性质,\(i=\sum_{d|i}\varphi(d)\)
那么令\(g(x)=x\)即可
\(h(i)=\sum_{d|i}\varphi(d)d*\frac{i}{d}=i^2\)
此时我们所需要的函数都求出来了,带入杜教筛式子\(g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{i=2}^ng(i)S(\lfloor \frac{n}{i} \rfloor)\)即可
叁、Min_25筛
和杜教筛一样,可以在压线性的复杂度下求出积性函数的前缀和,时间复杂度 \(O(\frac{n^{\frac{3}{4}}}{\log n})\)
它的使用条件是:\(f(x)\) 是一个积性函数; \(f(x)\) 在质数处取值可以被简单算出; 对于质数p,\(f(p^c)\) 是一个关于p的低阶多项式
step 1 分类
Min_25筛能在亚线性的复杂度下求出答案,其关键步骤就是按质数和合数分类,并枚举合数的最小质因子和质因子个数,即
其中 \(minp(i)\) 表示 \(i\) 的最小质因子。注意到一个合数的最小质因子一定小于 \(\sqrt n\) ,枚举得到巨大优化。
step 2 质数求解
- 注意,这里 \(g\) 和后面的 \(s\) 不记1的贡献,原因会在后面提到
设对于质数 \(p\),其中 f 在 p 处能被表示为 \(f(p)=\sum a_ip^i\) 的多项式,那么不妨对于每一项 \(p^k\) ,先统计出 \(\sum p^k\) 再进行计算
注意到,这里 \(g\) 存的函数值并不直接是原函数的值,而是某个能简单算出的函数且它在质数处的取值和原函数相同。这里直接写成多项式的形式是因为这个转化比较常见。
考虑一个 dp (太神仙了难以想到),设 \(g(n,j)\) 表示 \(n\) 范围内,所有质数或最小质因子大于第j个质数的所有数的k次方之和。最后要求的就是 \(g(n,j)\) ,其中 \(p_j\) 是最后一个 \(\leq \sqrt n\) 的质数
考虑从 \(g(n,j-1)\) 转移到 \(g(n,j)\) ,此时需要减去最小质因子是 \(p_j\) 的所有合数的贡献。
既然最小质因子是 \(p_j\) ,又幂函数是一个完全积性函数,那么我们直接提出它,分类:
初始值:\(g(n,0)=\sum_{i=2}^n i^k\)
从搜索的角度来看,我们会转移到的位置 \(n,\lfloor \frac{n}{p_1} \rfloor,\lfloor \frac{\lfloor \frac{n}{p_1} \rfloor}{p_2} \rfloor...\) ,又有 \(\lfloor \frac{\lfloor \frac{n}{p_1} \rfloor}{p_2} \rfloor=\lfloor \frac{n}{p_1p_2} \rfloor\)
发现上式只用到形如 $\lfloor \frac{n}{x} \rfloor $ ,\(x \leq n\) 位置上的 dp 值,这些值最多只有 \(2\sqrt n\) 种,只需要求出它们的值并直接 dp 即可。(注意到质数 \(p_j\) 的取值本来就在 \(\sqrt n\) 以下,所以不用特殊处理
step 3 合数求解
设 \(s(n,j)\) 表示 \(n\) 范围内最小质因子大于 \(p_j\) 的所有数的 \(f\) 值之和。接下来令 \(G(n)\) 表示 \(n\) 范围内所有质数的 \(f\) 值之和,从 \(g(n,j)\) 算出即可
同样的分类,质数贡献+合数贡献
注意枚举的 \(p_k \leq \sqrt n\),\(p_k^e \leq n\) 边界条件。
上文所说不记1的贡献就是原因在此:首先,对于积性函数 \(f(1)=1\)。则当 \(e=1\) 时,\(p_k^e\) 就是一个质数,已经统计过了,不能再加;当 \(e \neq 1\) 时,需要加上 \(p_k^e\) 的贡献。
(气死了,刚打完结果可能因为latex太多被卡退了,啥都没了全部重写……所以要养成随时保存的好习惯哦
一些实现细节看代码吧
Min_25筛模版
//Min_25筛模版
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5,inf=2e9,mod=1e9+7,inv2=500000004,inv6=166666668;
int n,m,p[maxn],cnt,vis[maxn];
void init(int n){
for(int i=2;i<=n;i++){
if(!vis[i]) p[++cnt]=i;
for(int j=1;p[j]*i<=n;j++){
vis[p[j]*i]=1;
if(i%p[j]==0) break;
}
}
}
int ind1[maxn],ind2[maxn];
//dp数组显然不能直接开到n,可以用map,但常数较大;这里用ind1 ind2分别存储x和n/x在dp数组的编号,下标<=sqrt(n)
int g1[maxn],g2[maxn],val[maxn],tot;
//一次项和二次项之和 所有n/x的值
int getid(int x){
return x<=m?ind1[x]:ind2[n/x];
}
int f(int x){
x%=mod;//细节
return (x*x-x)%mod;
}
int s(int n,int j){//递归求解s(n,j)
if(p[j]>n) return 0;//边界
int id1=getid(n),id2=getid(p[j]);
int res=((g2[id1]-g1[id1])-(g2[id2]-g1[id2])+mod+mod)%mod;
for(int k=j+1;k<=cnt&&p[k]<=n/p[k];k++){
for(int e=1,num=p[k];num<=n;e++,num=num*p[k]){
res=(res+f(num)*(s(n/num,k)+(e!=1)))%mod;
}
}
return res;
}
signed main(){
scanf("%lld",&n);
m=sqrt(n);
init(m);
for(int l=1,r;l<=n;l=r+1){//整除分块
r=n/(n/l);
val[++tot]=n/l;
if(val[tot]<=m) ind1[val[tot]]=tot;
else ind2[n/val[tot]]=tot;
int num=val[tot]%mod;//注意要取模!
g1[tot]=num*(num+1)%mod*inv2%mod-1;
g2[tot]=num*(num+1)%mod*(2*num+1)%mod*inv6%mod-1;//减掉1的贡献
}
for(int j=1;j<=cnt;j++){//先枚举第二维再枚举第一维
for(int i=1;i<=tot&&p[j]<=val[i]/p[j];i++){
int id1=getid(val[i]/p[j]),id2=getid(p[j-1]);//这样才能保证这里调用的g[id1]是存的j-1的信息,否则它都全被更新完了
g1[i]=(g1[i]-p[j]*(g1[id1]-g1[id2])%mod+mod)%mod;
g2[i]=(g2[i]-p[j]*p[j]%mod*(g2[id1]-g2[id2])%mod+mod)%mod;
}
}
printf("%lld",(s(n,0)+1)%mod);//最后加上1的贡献
return 0;
}
例题
[loj6235] 区间素数个数
因为Min_25筛是质数合数分开处理的,所有实际上我们只需要用第一步dp求出质数的0次方和即可
[loj053] 简单的函数
2的情况是特殊的,剩下在质数处的取值都是p-1。细节上处理一下即可。
注意到 \(\sum_p (p-1)\) 并不是完全积性函数,要分别处理(我居然错在这儿了!!

浙公网安备 33010602011771号