【数学】各种积性函数的线性筛法

【数学】各种积性函数的线性筛法

前置芝士:几种特殊的积性函数的定义及基本性质。

定义

积性函数:若函数f(x)满足f(x)=1x,yN+,gcd(x,y)=1 ,都有f(xy)=f(x)f(y),则f(x)为积性函数。

完全积性函数:若函数满足f(x)=1x,yN+,都有f(xy)=f(x)f(y),则f(x)为积性函数。

判定

特殊例子:(以下都是积性函数)

ϵ(n)=[n=1]n=1时为1,否则为0)

idk(n)=nk,当k=1时简记为id(n)

1(n)=1

σk(n)=d|ndk,当k=0时为因数个数,记为d(n)k=1时为因数和,记为σ(n)

ϕ(n)=i=1n[gcd(i,n)=1]

μ(n)={1    n=1(1)k    1k0    otherwise

对于函数之间运算产生的函数,有以下规律:

f(x)g(x)都是积性函数,则以下函数也是积性函数:

h(x)=f(xp)

h(x)=fp(x)

h(x)=f(x)g(x)

f(x)g(x)Dirichleth(x)=d|xf(x)g(gd)

有时在题目中,会要求筛出1106甚至1107的这些函数值,时间复杂度要求O(n),我们如何计算这些东西呢?

我们发现一个普遍的性质,这些函数当自变量取值为质数时,都可以简单地O(1)求出,于是我们将这个过程代入筛素数的线性筛,再尝试每次用质数(创造互质条件)和积性函数的性质求出函数值。

不会线性筛质数的自行前往:P3383 【模板】线性筛素数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

具体对于每一个函数将怎么做呢?

欧拉函数ϕ

直接枚举比x小的数显然不可行,我们知道欧拉函数有一种计算方法:

ϕ(x)=xpprime & p|xp1p

因为无论一个质数px的分解中出现多少次,它p1p的贡献都只有一次,结合到质数筛中我们每次都用一个质数去乘以一个其他数,可以分类讨论:

ϕ(x)=x1   if xprime

以及(p是线性筛中每次枚举的质数)

ϕ(xp)={ϕ(x)ϕ(p)    pxϕ(x)p    px

因为px时,满足gcd(p,x)=1,所以可以直接相乘。

px时,x中已有p,贡献就只有乘上了p倍。

由于线性筛一定会把每个数筛一次,所以保证范围内的数全都计算了ϕ值。

(代码中的ϕ(p)写作了p1,因为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];
        }
    }
}

莫比乌斯函数μ

ϕ的分析方法,我们不难发现,当自变量是一个质数的时候,其一定只有一个质因数。

μ(x)=1   if xprime

考虑到线性筛中一个任意数和一个质数相乘的情况,如果pi,那么pi一定含有超过一个pμ为0,如果pi,至少说明乘上p这一操作让i多了一个质因数,不管之前μ是否为0,将μ取反,一定满足其定义。

μ(ip)={μ(i)μ(p)=μ(i)    pi0    pi

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];
        }
    }
}

因数和σ

精彩的部分来了,本人第一次看到时也十分震撼。

方法源自ldysy2102 - 博客园的博客线性筛约数个数、约数和的新方法 - ldysy2102 - 博客园 (cnblogs.com)

首先当p为质数时,因数只有1p两个,因数和σ(p)=p+1

由唯一分解定理,一个数可以被分解为:

x=i=1 & piprimekpici

这个数的因数就是这些质因数任意次数的任意组合,每个质因数都可以选择0ci个。所以

每个因数就是质因数的任意次方乘起来,是:

σ(x)=(1+p1+p12+..+p1c1)(1+p2+p22+..+p2c2)...(1+pk+..+pkck)

(1+p2+..)..(1+pk+..)=T,再次讨论线性筛中一个质数乘上一个任意数的情况,在pi时,我们钦定乘上的质因数是p1

σ(xp1)=(1+p1+..+p1c1+1)T=σ(x)+p1c1+1T

σ(xp1)=(1+p1+..+p1c11)T=σ(x)p1c1T

两个柿子只有T前的系数不一样,将式乘上p1再加上,神奇的事情发生了:

p1σ(xp1)+σ(xp1)=(p1+1)σ(x)

σ(xp1)=(p1+1)σ(x)p1σ(xp1)

这样,在知道p1x之后我们就可以在枚举质数时完成计算。

由于这些函数都是积性函数,所以当px时的处理方法都是一样的。

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,唯一分解后得到

d(x)=(c1+1)(c2+1)...(ck+1)

d(x)=(c1+1)T

d(xp1)=(c1+2)T

d(xp1)=c1T

所以

d(xp1)+d(xp1)=2d(x)

所以最终得到:

d(xp1)=2d(x)d(xp1)

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;
        }
    }
}
posted @   The_Last_Candy  阅读(100)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示