数论函数
参考博客:command_block,ANIG
一.定义
0.一些记号
\([S]\) 为 艾弗森括号,定义如下:
1.一些数论函数
\(\varphi(n)=\sum\limits_{i=1}^{n}[\gcd(i,n)=1]\),即 \(n\) 以内与 \(n\) 互质的数的个数,叫做欧拉函数,念做 /fa?/。
\(\mu(n)=\begin{cases}1 &n=1\\0 &\exists i\in[1,m],k_i>1 \\(-1)^m & \forall i \in[1,m],k_i=1 \end{cases}\),记 \(n=\prod\limits_{i=1}^{m}p_i^{k_i}\),叫做莫比乌斯函数,念做 /mju:/。
下面的表从此博客copy来:
函数名称 | 符号 | 定义 | 积性 | 卷积 |
---|---|---|---|---|
恒等函数 | \(I(n)\) 或 \(1(n)\) | \(1\) | 完全积性函数 | |
元函数 | \(\varepsilon(n)\) 或 \(e(n)\) | \([n=1]\) | 完全积性函数 | \(\varepsilon=\mu*1\) |
单位函数 | \(id(n)\) | \(n\) | 完全积性函数 | \(id=\varphi*1\) |
幂函数 | \(id^x(n)\) | \(n^x\) | 完全积性函数 | |
莫比乌斯函数 | \(\mu(n)\) | 见上 | 积性函数 | |
欧拉函数 | \(\varphi(n)\) | 见上 | 积性函数 | \(\varphi=\mu*id\) |
约数个数函数 | \(d(n)\) 或 \(\sigma_0(n)\) | n的约数个数 | 积性函数 | \(d=1*1\) |
约数和函数 | \(\sigma(n)\) | n的约数和 | 积性函数 | \(σ=id*1\) |
除数函数 | \(\sigma^k(n)\) | n的约数k次方和 | 积性函数 | \(σ^k=id^k*1\) |
注:\(\varepsilon\) 念做 /'eps?l?n/,\(\sigma\) 念做 /'s?ɡm?/。
2.积性函数
如果有数论函数 \(f\),满足:
若 \(\gcd(n,m)=1\),则 \(f(nm)=f(n)f(m)\)
则 \(f\) 为积性函数。
如果函数 \(g\) 满足:
对于对于任意的 \(n,m\),有 \(g(nm)=g(n)g(m)\)
则 \(g\) 为完全积性函数
3.狄利克雷卷积
如果有两个数论函数 \(f\) 和 \(g\),则它们的狄利克雷卷积 \(f*g\) 为:
狄利克雷卷积满足:
-
交换律:\(f*g=g*f\)
-
结合律:\(f*(g*h)=(f*g)*h\)
-
若 \(f\) 和 \(g\) 是积性函数,则 \(f*g\) 也是积性函数
二.性质
\(\varphi\) 相关
-
若 \(p\) 为质数,则 \(\varphi(p)=p-1\).
-
若 \(p\) 为质数且 \(k\ge1\),则 \(\varphi(p^k)=p^k-p^{k-1}\).
-
设 \(n=\prod\limits_{i=1}^{m}p_i^{k_i}\),则 \(\varphi(n)=n\times\prod\limits_{i=1}^{m}\frac{p_i-1}{p_i}\).
-
若 \(p\in\mathbb{P}\),则: \(\varphi(n)=\begin{cases}p\varphi(\frac{n}{p}) &p\mid n\text{ 且 }p^2\nmid n \\(p-1)\varphi(\frac{n}{p}) &p^2\mid n\end{cases}\)
\(\mu\) 相关
- \(\sum\limits_{d\mid n}\mu(d)=[n=1]\),同理 \([gcd(i,j)=1]=\sum\limits_{d\mid gcd(i,j)}\mu(d)\).
三.算法
1.素数筛子
埃氏筛
很好用的筛子,不过已经线性筛写习惯了,不会写了。
思想很简单,从 \(1\) 到 \(n\) 枚举,如果当前数没被标记,就标记为素数,并把 \(n\) 以内它的倍数标记为合数。
复杂度 \(O(n\log\log n)\)。
点击查看代码
ll prime[N],tot;
bool vis[N];
void shai(ll n){
for(int i=2;i<=n;i++){
if(!vis[i]){
prime[++tot]=i;
for(int j=2;i*j<=n;j++){
vis[i*j]=1;
}
}
}
}
线性筛
神筛子,必须会默写!
思想是只用一个数的最小值质因子筛它,具体见代码实现。
复杂度 \(O(n)\)。
线性筛可以筛积性函数,或者一些其他的东西:
我们设 \(p\mid n,p\in\mathbb{P}\),对于本节的所有 \(n\) 成立。
筛 \(\varphi\)
我们知道 \(\varphi\) 满足如下公式:
于是我们可以在线性筛过程中,发现 \(i\) 为质数时直接计算 \(\varphi(i)\);
用 \(prime_j\) 筛 \(i\times prime_j\) 时,根据是否有 \(prime_j\mid i\) 来计算 \(\varphi(i\times prime_j)\)。
筛 \(\mu\)
我们知道 \(\mu\) 满足如下公式:
同样的计算方法。
筛最大质因子
设 \(mxp(n)\) 为 \(n\) 的最大质因子,且 \(p\) 为 \(n\) 的最小质因子,有:
这样就可以用线性筛筛了。
筛一般积性函数
设 \(f\) 为积性函数,且 \(f(p^k)\) 可以快速求,于是就可以用线性筛 \(O(n)\) 筛出 \(f\)。
具体地,我们设 \(v_i\) 为 \(i\) 的最小质因数,而 \(c_i\) 满足 \(v_i^{c_i}\mid i\;,\;v_i^{c_i+1}\nmid i\),即 \(v_i^{c_i}\) 为 \(i\) 的分解质因数中,属于 \(v_i\) 那一整个幂。
\(c_i\) 满足以下计算式:
我们设 \(g_i=v_i^{c_i}\),\(g_i\) 满足以下计算式:
于是乎,我们枚举 \(i\in[2,n]\),若 \(i=g_i\),即 \(i=v_i^{c_i}\),可以快速求出 \(f(i)\);否则,有 \(f(i)=f(g_i)\cdot f(\frac{i}{g_i})\),而因为我们按顺序枚举,后两者已经算过了。
下面是封装好的多功能线性筛:
点击查看代码
ll prime[N],v[N],tot;
ll phi[N],mu[N],mxp[N];
ll c[N],g[N],f[N];
void shai(ll n){
mu[1]=phi[1]=v[1]=mxp[1]=1;
for(int i=2;i<=n;i++){
if(!v[i]){
v[i]=i;prime[++tot]=i;
phi[i]=i-1;mu[i]=-1;mxp[i]=i;
c[i]=1;g[i]=i;
}
for(int j=1;j<=tot;j++){
if(prime[j]>v[i]||prime[j]>n/i) break;
ll x=i*prime[j];
v[x]=prime[j];
if(i%prime[j]==0){
phi[x]=prime[j] * phi[i];mu[x]=0;
c[x]=c[i]+1;g[x]=g[i]*prime[j];
}else{
phi[x]=(prime[j]-1)*phi[i];mu[x]=-mu[i];
c[x]=1;g[x]=prime[j];
}
mxp[x]=mxp[i];
}
}
f[1]=calc(1);
for(int i=2;i<=n;i++){
if(i==g[i]) f[i]=calc(i);
else f[i]=f[g[i]] * f[i/g[i]];
}
return;
}
2.前缀和筛子
(一)杜教筛
模板题:P4213 【模板】杜教筛
首先我们证一个东西:
其中 \(S(n)=\sum\limits_{i=1}^{n}f(i)\)。
证明:先把式子左边写出来:
用两个变量 \(i\) 和 \(j\) 来代替 \(d\) 和 \(\frac{d}{i}\),进行循环,则 \(ij\) 就对应原式中的 \(i\),所以有 \(j\le \lfloor\frac{n}{i}\rfloor\):
把 \(g(i)\) 提出去,剩余的就是 \(f\) 的前缀和:
证毕。
假如我们要求一个函数 \(f\) 的前缀和 \(S\),即 \(S(n)=\sum\limits_{i=1}^{n}f(i)\),其中 \(n\) 很大。
我们可以考虑构造另一个数论函数 \(g\),然后通过刚才证的结论,构造一个 \(S(n)\) 关于 \(S(\lfloor\frac{n}{i}\rfloor)\) 的转移式:
如果 \(g\) 的前缀和可以 \(O(1)\) 算,\(g(i)\) 的单点值也能 \(O(1)\) 算,这个式子就可以在 \(O(n^{\frac{2}{3}})\) 内算出来。
具体地,我们先预处理出 \(k=n^{\frac{2}{3}}\),\(S(1)\sim S(k)\) 的值。
对于某一个询问,前半段直接计算,后半段数论分块,写成记忆化搜索的形式,往前搜索就行了。
一些常见的筛:
1.筛 \(\varphi\)
我们知道 \(\varphi * 1=id\),于是我们取 \(g=1\),有:
也就是:
就OK了,直接筛就行。
2.筛 \(\mu\)
我们知道 \(\mu * 1=\varepsilon\),于是我们取 \(g=1\),有:
也就是:
min_25筛
单独写一篇博客了,在这里。
3.拉格朗日插值
给定 \(n\) 个点对 \((x_1,y_1)\dots(x_n,y_n)\),可以确定一个 \(n-1\) 次的多项式 \(f(x)\),满足 \(\forall i\in[1,n],f(x_i)=y_i\)。
求 \(f(m)\) 的值,或求出 \(f\) 的各项系数。
(一).求值
这部分内容主要来自 此篇博客
我们想办法对于每一对 \((x_k,y_k)\),构造出一个 \(n-1\) 次的函数 \(g_k(x)\),满足:
也就是一个专门对付第 \(k\) 个点值的函数,对其他点值无影响。
我们令 \(f(x)=\sum\limits_{k=1}^{n}g_k(x)\),就能够符合题目要求了。
我们先构造出一个满足后半部分的函数:
容易验证,当 \(x= x_i,i\neq k\) 时,\(t_k(x)=0\)。
如何构造出一个满足全部条件的 \(g_k(x)\) 呢?我们会发现:
这个函数,满足当\(x= x_i,i\neq k\) 时为 \(0\),而当 \(x=x_k\) 时,分子分母相等,故式子等于 \(1\)。
这还不够!我们要求 \(x=x_k\) 时式子值为 \(y_k\)!
那我们把 \(y_k\) 乘上去不就行了……
于是我们这样构造出 \(g_k(x)\):
所以 \(f(x)\) 终于可以表示出来了!
稍微把变量名微调,然后把 \(x=m\) 带进去,我们就有了拉格朗日插值的最终式子:
于是乎,我们就能 \(O(n^2)\) 求点值了,当然通过一些预处理和优化,能做到 \(O(n)\)。
(二).计算系数
这部分内容主要来自这篇博客
对于一个多项式 \(f(x)\),我们记
为 \(f\) 的 \(x^k\) 项系数。(这里是[]中括号,不知道为什么渲染成了上取整……)
我们提出拉插式子中的系数:
可以 \(O(n^2)\) 预处理出 \(a\) 数组:
for(int i=0;i<n;i++){
a[i]=1;
for(int j=0;j<n;j++){
if(i==j) continue;
(a[i] *= (x[i]-x[j])%mod )%=mod;
}a[i] = qpow(a[i],mod-2) * y[i]%mod;
}//求a
接下来,我们求一个多项式
的各项系数,这个可以 \(O(n^2)\) 求:
g[0]=1;//最初,是个0次的多项式 "1"
for(int i=0;i<n;i++){//给它乘上一个 (m-x[i])
for(int j=i+1;j>=1;j--){
g[j] = ( g[j-1] - g[j] * x[i]%mod ) %mod;
}g[0]= g[0] * (-x[i])%mod;
}//求g
这时候,原拉插式子就变成了:
我们设\(h(m)=\dfrac{g(m)}{m-c}\),尝试求出 \(h\) 的各项系数:
我们有 \((m-c)h(m)=g(m)\),两边系数对应,有:
于是就可以递推算出来:
for(int i=0;i<n;i++){
ll inv=qpow(-x[i],mod-2);
if(!inv){//x[i]=0
for(int j=0;j<n;j++) h[j]=g[j+1];
}else{
h[0]=g[0] * inv%mod;
for(int j=1;j<n;j++){
h[j] = (g[j] - h[j-1])%mod *inv%mod;
}
}
for(int j=0;j<n;j++){
(f[j] += a[i] * h[j]%mod) %=mod;
}
}
我们理一理:
- \(a\) 有 \(n\) 项;
- \(g\) 是 \(n\) 次的,故有 \(n+1\) 项;
- \(h\) 是 \(n-1\) 次的,故有 \(n\) 项;
- \(f\) 是 \(n-1\) 次的,故有 \(n\) 项。
总时间复杂度 \(O(n^2)\)。
4.自然数幂和
求:
分为两种情况讨论:
(一). n 小 k 大
因为 \(i^k\) 是完全积性函数,所以可以线性筛筛出来。
若 \(n\) 以内素数个数为 \(p\),筛出来的复杂度就是 \(p\log k\)。
因为 \(n\) 很小,所以直接算前缀和就行了。
(二). n 大 k 小
我们观察 \(k=0,1,2,3\) 的公式:
于是我们发现:当指数为 \(k\) 时,求和公式是一个 \(k+1\) 次的多项式。
cy言:这是积分积出来的,所以多一次。「积分多一次,求导少一次」
所以我们如果想确定指数为 \(k\) 时的通项公式,需要 \(k+2\) 个点值。
于是乎,我们拉格朗日插值插出来。
我们记 \(f(n)=\sum\limits_{i=1}^{n}i^k=\sum\limits_{i=0}^{k+1}f_ix^i\),取 \(k+2\) 个点值 \((x_1,y_1),(x_2,y_2)\dots(x_{k+2},y_{k+2})\)。
为了好算,我们取 \(x_i=i\),有:
这里面 \(y\) 的求值可以用线性筛做到 \(O(p\log k)\)(其中 \(p\) 为 \(k\) 以内的质数个数)。
而后面那一大坨,可以预处理,就能做到 \(O(k)\)。
这样子的话,总复杂度就是 \(O(p\log k + k)\),非常优秀。
当然题目要求没那么严的话直接枚举更加直观,\(O(k^2)\) 的复杂度。
特别鸣谢:Qcfff
四.推式子技巧
1. 总结性式子
(一)
详细解释在数论习题这篇博客中P2568 GCD这个题里提到。
(二)
\(\color{red}\text{上述两个式子,把 } j \text{ 的上界换成 }m\)
借助了莫反,详细解释在数论习题这篇博客中P2522 [HAOI2011] Problem b这个题里提到。
到这里,这个式子已经可以两次数论分块做出来了,但还有优化空间:
注意到:\(dk\le\min(n,m)\),而 \(\lfloor\frac{n}{dk}\rfloor \lfloor\frac{m}{dk}\rfloor\) 同时和 \(d\,,\,k\) 相关,也只和 \(dk\) 相关,于是可以考虑枚举 \(T=dk\):
发现 \(\sum\limits_{k\mid T}\mu(k)\frac{T}{k}\) 就是 \(\mu * id=\varphi\),所以:
一次数论分块即可。
2. 其他小技巧
-
\(\sum\limits_{d\mid n}\mu(d)=[n=1]\),同理 \([gcd(i,j)=1]=\sum\limits_{d\mid gcd(i,j)}\mu(d)\).
-
\(\sum\limits_{i=1}^{n} f(i)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{f(i)}1 =\sum\limits_{j=1}^{f(n)}\sum\limits_{i=1}^{n}[j\le f(i)]\),来源于P5170 【模板】类欧几里得算法
-
\(nm=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[\gcd(i,j)=1]\min(\lfloor\frac{n}{i}\rfloor,\lfloor\frac{m}{i}\rfloor)\),无意间发现的,不知道有什么用。
-
\(d(nm)=\sum\limits_{i\mid n}\sum\limits_{j\mid m}[\gcd(i,j)=1]\)、
-
\(\varphi(ij)=\dfrac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))}\)