线性筛求积性函数

线性筛求积性函数

(更新中)

定理证明

积性函数都可用线性筛法求解
一个大于1的数n可以写成 \(n=\prod a_{i} ^{p_{i}}\), 而 \(a_{i} ^{p_{i}}\) 之间互质,由于积性函数的性质,所以\(f(x)=f\prod a_{i} ^{p_{i}}=\prod f(a_{i} ^{p_{i}})\)

线性筛

void init()
{
    for (int i = 2; i < N; i ++)
    {
        if (!st[i]) 
        {
            prime[cnt ++] = i;
            // 1
        }
        for (int j = 0; prime[j] * i <= n; j ++)
        {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                // 2
                break;
            }
            // 3
        }
    }
}

线性筛的原理就不解释了,我们要知道的是每个数是被自己最小的质因子筛掉的就行,这里我们看1,2,3三个地方分别是什么含义。

标号1是质数的时候。

标号2是被最小的质数筛掉,但是这个数中这个质因子出现次数>1,即存在 \(i | prime[j]^{2}\),

标号3和2最大的区别就是,标号3的位置的 \(i\) 和 \(prime[j]\) 互质。互质可以通过用积性函数的定理去相乘。所有函数都是 \(f[i*prime[j]] = f[i] * f[prime[j]]\)

那么接下来分别考虑每个函数在这三种情况时候的函数就可以轻松写出用质数筛求他们的方法。

线性筛求欧拉函数

欧拉函数的定义是: 小于或等于n的正整数中与n互质的数的数目

那么很快可以知道n为质数,也就是上述的1位置是 \(\varphi(n)=n-1\)。

3位置上面说过 \(f[i*prime[j]] = f[i] * f[prime[j]]\)

对于2的位置上,我们先设p是n最小质因子,\(a=n/p\)。在 1 到 a 中和 a 互质的数是 \(\varphi(a) - 1\), a 和 x不互质, 所以 a 和 x + a 也一定互质, 那么在 a+1 到 2a 的范围内和 a 互质的数的个数也是 \(\varphi(a) - 1\),所以 \(\varphi(n) = \varphi(a) * p\)。

代码如下

void init()
{
    phi[1] = 1;
    for (int i = 2; i < N; i ++) 
    {
        if (!st[i]) 
        {
            prime[cnt ++] = i;
            phi[i] = i - 1;
            // 1
        }
        for (int j = 0; prime[j] * i <= n; j ++)
        {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                // 2
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
            // 3
        }
    }
}

线性筛求莫比乌斯函数

\(\mu(\mathrm{n})=\left\{\begin{array}{ll} 1 & \mathrm{n}=1 \\ 0 & \exists \mathrm{p}, \mathrm{~p}^{2} \mid \mathrm{n} \\ (-1)^{k} & \text { k 是质因子个数 } \end{array}\right. \)

从函数就可以看出,n是质数的话就是 -1, \(\mu(n) = 0\) 的时候对应线性筛 标号2 的地方,标号3的地方每次多乘以一个质因子,所以 \(\mu[i * prime[j]] = -\mu[i]\)。

代码:

void init()
{
    mu[1] = 1;
    for (int i = 2; i < N; i ++) 
    {
        if (!st[i]) 
        {
            prime[cnt ++] = i;
            mu[i] = -1;
            // 1
        }
        for (int j = 0; prime[j] * i <= n; j ++)
        {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                mu[i * prime[j]] = 0;
                // 2
                break;
            }
            mu[i * prime[j]] = -mu[i];
            // 3
        }
    }
}

线性筛求约数个数

首先我们用式子表示一下n的约数个数, \(n=p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{k}^{a_{k}}\), 所以n的约数个数就是 \(\prod(a_{1} + 1)(a_{2} + 1)...(a_{k} + 1)\), 如果 \(x = y * p\), 那么他们的约数个数就是\((a_{p} + 1) / (a_{p} + 2)\), \(a_{p}\) 代表质数p的指数。因为线性筛中每个数都是被自己最小的质因子筛掉,所以只需要额外开个数组存每个数的最小质因子的数量就行。
我们用\(d\)数组来记录约数个数, 用\(num\)数组来记录最小质因子的个数。
标号1质数的部分
\(d[i]=2, num[i] = 1\).
标号2的地方是
\(num[i * prime[j]] = num[i] + 1, d[i * prime[j]] = d[i] / (num[i] + 1) * (num[i * prime[j]] + 1)\)
标号3的地方是
\(num[i * prime[j]] = 1, d[i * prime[j]] = d[i] * d[prime[j]]\)

void init()
{
    d[1] = 1;
    for (int i = 2; i < N; i ++) 
    {
        if (!st[i]) 
        {
            prime[cnt ++] = i;
            d[i]=2, num[i] = 1;
            // 1
        }
        for (int j = 0; prime[j] * i <= n; j ++)
        {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                // 2
                num[i * prime[j]] = num[i] + 1;
                d[i * prime[j]] = d[i] * (num[i * prime[j]] + 1) / (num[i] + 1);
                break;
            }
            // 3
            num[i * prime[j]] = 1;
            d[i * prime[j]] = d[i] * d[prime[j]]
        }
    }
}

线性筛求约数和

这个和求约数个数差不多, 我们先来看约数和是如何表示的。
\(n=p_{1}^{a_{1}}p_{2}^{a_{2}}...p_{k}^{a_{k}}\), n的约数和就是 \(\prod {(1+p_{i}+p_{i}^{2}+...+p_{i}^{a_{i}})}\) 用等比数列求和可以化简,那么我们要记录的就是每个数最小质因子的等比数列。

我们用sp数组记录最小质因子的等比数列, 用sd数组记录约数和。
标号2的部分,\(sp[i * prime[j]]=sp[i] * prime[j] + 1, sd[i * prime[j]]=sd[i] / sp[i] * sp[i * prime[j]];\)

代码如下:

void init()
{
    sp[1] = 1;
    for (int i = 2; i < N; i ++) 
    {
        if (!st[i]) 
        {
            prime[cnt ++] = i;
            d[i]=2, num[i] = 1;
            sd[i] = sp[i] = i + 1;
            // 1
        }
        for (int j = 0; prime[j] * i <= n; j ++)
        {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0)
            {
                // 2
                sp[i * prime[j]]=sp[i] * prime[j] + 1;
                sd[i * prime[j]]=sd[i] / sp[i] * sp[i * prime[j]];
                break;
            }
            // 3
            sd[i * prime[j]] = sd[i] * sd[prime[j]];
            sp[i * prime[j]] = 1 + prime[j];
        }
    }
}
posted @ 2024-06-29 21:43  からし  阅读(129)  评论(0)    收藏  举报