数学各种积性函数的线性筛法
Posted fanghaoyu801212
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学各种积性函数的线性筛法相关的知识,希望对你有一定的参考价值。
【数学】各种积性函数的线性筛法
前置芝士:几种特殊的积性函数的定义及基本性质。
定义
积性函数:若函数\\(f(x)\\)满足\\(f(x) = 1\\)且\\(\\forall x,y \\in N^+,gcd(x,y) = 1\\) ,都有\\(f(xy) = f(x)f(y)\\),则\\(f(x)\\)为积性函数。
完全积性函数:若函数满足\\(f(x) = 1\\)且\\(\\forall x,y \\in N^+\\),都有\\(f(xy) = f(x)f(y)\\),则\\(f(x)\\)为积性函数。
判定
特殊例子:(以下都是积性函数)
\\(\\epsilon(n) = [n = 1]\\) (\\(n = 1\\)时为1,否则为0)
\\(id_k(n) = n^k\\),当\\(k = 1\\)时简记为\\(id(n)\\)
\\(1(n) = 1\\)
\\(\\sigma_k(n) = \\sum_d|nd^k\\),当\\(k = 0\\)时为因数个数,记为\\(d(n)\\),\\(k = 1\\)时为因数和,记为\\(\\sigma(n)\\)
\\(\\phi(n) = \\sum_i = 1^n [gcd(i,n) = 1]\\)
对于函数之间运算产生的函数,有以下规律:
若\\(f(x)\\)和\\(g(x)\\)都是积性函数,则以下函数也是积性函数:
有时在题目中,会要求筛出\\(1 \\to 10^6\\)甚至\\(1 \\to 10^7\\)的这些函数值,时间复杂度要求\\(O(n)\\),我们如何计算这些东西呢?
我们发现一个普遍的性质,这些函数当自变量取值为质数时,都可以简单地\\(O(1)\\)求出,于是我们将这个过程代入筛素数的线性筛,再尝试每次用质数(创造互质条件)和积性函数的性质求出函数值。
不会线性筛质数的自行前往:P3383 【模板】线性筛素数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
具体对于每一个函数将怎么做呢?
欧拉函数\\(\\phi\\)
直接枚举比\\(x\\)小的数显然不可行,我们知道欧拉函数有一种计算方法:
因为无论一个质数\\(p\\)在\\(x\\)的分解中出现多少次,它\\(\\frac p - 1p\\)的贡献都只有一次,结合到质数筛中我们每次都用一个质数去乘以一个其他数,可以分类讨论:
以及(\\(p\\)是线性筛中每次枚举的质数)
因为\\(p \\mid x\\)时,满足\\(gcd(p,x) = 1\\),所以可以直接相乘。
\\(p \\nmid x\\)时,\\(x\\)中已有\\(p\\),贡献就只有乘上了\\(p\\)倍。
由于线性筛一定会把每个数筛一次,所以保证范围内的数全都计算了\\(\\phi\\)值。
(代码中的\\(\\phi(p)\\)写作了\\(p - 1\\),因为\\(p\\)是质数,两者是等价的,后面的函数也一样)
Code
inline void init()
phi[1] = 1;
for(R int i = 2;i <= N - 1;i++)
if(!vis[i])
phi[i] = i - 1;
prime[++tot] = i;
for(R int j = 1;j <= tot && i * prime[j] < N;j++)
vis[prime[j] * i] = 1;
if(!(i % prime[j]))
phi[prime[j] * i] = (prime[j] - 1) * phi[i];
break;
phi[prime[j] * i] = phi[i] * prime[j];
莫比乌斯函数\\(\\mu\\)
同\\(\\phi\\)的分析方法,我们不难发现,当自变量是一个质数的时候,其一定只有一个质因数。
即
考虑到线性筛中一个任意数和一个质数相乘的情况,如果\\(p \\mid i\\),那么\\(p * i\\)一定含有超过一个\\(p\\),\\(\\mu\\)为0,如果\\(p \\nmid i\\),至少说明乘上\\(p\\)这一操作让\\(i\\)多了一个质因数,不管之前\\(\\mu\\)是否为0,将\\(\\mu\\)取反,一定满足其定义。
Code
inline void init()
miu[1] = 1;
for(R int i = 2;i <= N - 1;i++)
if(!vis[i])
miu[i] = -1;
prime[++tot] = i;
for(R int j = 1;j <= tot && i * prime[j] < N;j++)
vis[prime[j] * i] = 1;
if(!(i % prime[j]))
miu[prime[j] * i] = 0;
break;
miu[prime[j] * i] = -miu[i];
因数和\\(\\sigma\\)
精彩的部分来了,本人第一次看到时也十分震撼。
方法源自ldysy2102 - 博客园的博客线性筛约数个数、约数和的新方法 - ldysy2102 - 博客园 (cnblogs.com)
首先当\\(p\\)为质数时,因数只有\\(1、p\\)两个,因数和\\(\\sigma(p) = p + 1\\)
由唯一分解定理,一个数可以被分解为:
这个数的因数就是这些质因数任意次数的任意组合,每个质因数都可以选择\\(0 \\to c_i\\)个。所以
每个因数就是质因数的任意次方乘起来,是:
令\\((1 + p_2 + ..)..(1 + p_k + ..) = T\\),再次讨论线性筛中一个质数乘上一个任意数的情况,在\\(p \\mid i\\)时,我们钦定乘上的质因数是\\(p_1\\):
两个柿子只有\\(T\\)前的系数不一样,将\\(②\\)式乘上\\(p_1\\)再加上\\(①\\),神奇的事情发生了:
这样,在知道\\(p_1\\)和\\(x\\)之后我们就可以在枚举质数时完成计算。
由于这些函数都是积性函数,所以当\\(p \\nmid x\\)时的处理方法都是一样的。
Code
inline void init()
sig[1] = 1;
for(int i = 2;i < N;i++)
if(!vis[i])
prime[++tot] = i;
sig[i] = i + 1;
for(int j = 1;j <= tot && i * prime[j] < N;j+)
vis[i * prime[j]] = 1;
if(!(i % prime[j]))
sig[i * prime[j]] = ((prime[j] + 1) * sig[i] % MOD - prime[j] * sig[i / prime[j]] % MOD + MOD) % MOD;
break;
sig[i * prime[j]] = sig[i] * (prime[j] + 1);
因数个数d
同理,首先发现当\\(p\\)为质数时,因数个数\\(d(p) = 2\\)
对于\\(x\\),唯一分解后得到
所以
所以最终得到:
Code
inline void init()
d[1] = 1;
for(int i = 2;i < N;i++)
if(!vis[i])
prime[++tot] = i;
d[i] = 2;
for(int j = 1;j <= tot && i * prime[j] < N;j+)
vis[i * prime[j]] = 1;
if(!(i % prime[j]))
d[i * prime[j]] = 2 * d[i] - d[i / prime[j]];
break;
d[i * prime[j]] = d[i] * 2;
积性函数筛法
积性函数筛法
很多常用的数论函数都是积性函数,而在题目中,我们常常需要线性(甚至更高)的筛法。
对于积性函数,我们可以在筛素数的基础上稍加修改,即可完成线性筛。
首先,注意到积性函数的特点:
[
f(xy)=f(x) imes f(y)
]
而可以线性筛的积性函数,需要知道以下两个式子的快速求法:
[
f(p)=?quad f(p^k)=?\pin prime
]
其中, (f(p)) 大多是直接定义,(f(p^k)) 大多是递归定义。
我们来回忆一下素数筛的过程:
inp[0]=inp[1]=1;
for(int i=2;i<=n;i++){
if(!inp[i]){
prime[++tot]=i;
}
for(int j=1;j<=tot && i*prime[j]<=n;j++){
int tp=prime[j]*i;
inp[tp]=1;
if(i%prime[j]==0){
break;
}
}
}
在线性筛素数的基础上,我们可以进行线性筛的修改。
首先,对于判定的质数 (p) ,可以直接给出定义的值。
之后,对于 (i\%p eq0) ,由于 (i) 和 (p) 互质,可以直接用积性函数性质推得。
然后,对于 (i\%p == 0) :
即 (i) 内的最小素因子是 (p) ,此刻可以将 (i) 内的素因子都除掉,然后就可以用积性函数的性质来递推了。为此,我们要记录一个最小质因子的幂次 (low_i) 。
那么递推式就可以表示为:(f(i imes p)=f(i/low_i) imes f(low_i imes p)) 。
此处还有一个特殊的判定,当 (i==low_i) 时,上式相当于没推,所以我们要用 (f(p^k)) 的递推来计算。
那么代码如下:
inp[0]=inp[1]=1;
f[1]=1;
for(int i=2;i<=n;i++){
if(!inp[i]){
prime[++tot]=i;
f[i]=对质数的定义式;
low[i]=i;
}
for(int j=1;j<=tot && i*prime[j]<=n;j++){
int tp=prime[j]*i;
inp[tp]=1;
if(i%prime[j]==0){
if(i!=low[i])
f[tp]=f[i/low[i]]*f[low[i]*prime[j]];
else
f[tp]=对p的次幂的定义式;
low[tp]=low[i]*prime[j];
break;
}
f[tp]=f[i]*f[prime[j]];
low[tp]=prime[j];
}
}
缺点很明显,比较耗空间。(但是题目会给够的
当需要线性筛很多个积性函数时,可以同时进行。
这种基于素数筛的线性筛法,有时不止对积性函数有用,对于一些和素数有关的函数也可以筛出,具体在我写的莫比乌斯反演中有例子。
(frak by;thorn\_)
以上是关于数学各种积性函数的线性筛法的主要内容,如果未能解决你的问题,请参考以下文章