积性函数及其筛法

积性函数及其筛法

前置知识

一些定理

质数筛法 - Cattle_Horse

互质:公约数只有 \(1\) 的两个整数叫做互质整数,也称这两个整数互质

算术函数(数论函数):定义在所有正整数集上的函数称为算术函数(数论函数)

积性函数(乘性函数):对于任意互质的整数 \(a\)\(n\) 有性质 \(f(a\times b)=f(a)\times f(b)\) 的数论函数

完全积性函数:对于任意整数 \(a\)\(b\) 有性质\(f(a\times b)=f(a)\times f(b)\)的数论函数

算术基本定理(唯一分解定理):任何一个大于 \(1\) 的自然数 \(N\),如果 \(N\) 不为质数,那么 \(N\) 可以唯一分解成有限个质数的乘积 \(N=P_1^{a_1}P_2^{a_2}P_3^{a_3}\cdots P_n^{a_n}\),这里 \(P_1<P_2<P_3\cdots<P_n\) 均为质数,其中指数 \(a_i\) 是正整数。这样的分解称为 \(N\) 的标准分解式。

积性函数的重要性质

  • \(f\) 为积性函数,则 \(f(1)=1\)
    证明:\(\forall n \in \mathbb{N^+}\) ,则有 \(gcd(n,1)=1\),所以 \(f(n)=f(n)\times f(1)\),因此 \(f(1)=1\)

  • (由算术基本定理推得)对于整数 \(n\) ,若 \(f\) 是积性函数,则\(f(n)=f(p_1^{a_1})\times f(p_2^{a_2})\times \cdots \times f(p_n^{a_n})\)

  • \(f(x)\)\(g(x)\) 均为积性函数,则以下函数也为积性函数

    • \(h(x)=f(x^p)\)
    • \(h(x)=f^p(x)\)
    • \(h(x)=f(x)\times g(x)\)
    • \(h(x)=\sum_{d\mid x}f(d)\times g(\dfrac{x}{d})\)

常见的积性函数

欧拉函数 \(\varphi(n)\):在数论中,对于正整数 \(n\),欧拉函数是 \(1\sim n\) 中与 \(n\) 互质的数的个数,即 \(\varphi(n)=\sum_{x=1}^{n}[gcd(x,n)=1]\)

莫比乌斯函数 \(\mu(i)\)\(\mu(n)=\left\{\begin{array}{ll} 1 & n=1 \\ 0 & n \text { 含有平方因子 } \\ (-1)^{k} & k \text { 为 } n \text { 的本质不同质因子个数 } \end{array}\right.\)

正整数 \(n\) 的约数(正因子)个数 \(d(n)\)\(\sigma_0(n)\)\(\sigma_0(n)=\sum_{d \mid n} 1\)

正整数 \(n\) 的所有约数(正因子)之和 \(\sigma_1(n)\)\(\sigma_1(n)=\sum_{d \mid n} d\)

正整数 \(n\) 的所有约数(正因子)\(k\) 次幂之和 \(\sigma_k(n)\)\(\sigma_k(n)=\sum_{d \mid n} d^k\)

常见的完全积性函数

常函数 \(I(n)\)\(I(n)=1\)

单位函数 \(id(n)\)\(id(n)=n\)

幂函数 \(id_k(n)\)\(id_k(n)=n^k\)

元函数 \(\epsilon(n)\)\(\epsilon(n)=[n=1]\)

欧拉函数

定义

欧拉函数 \(\varphi(n)\):在数论中,对于正整数 \(n\),欧拉函数是 \(1\sim n\) 中与 \(n\) 互质的数的个数,即 \(\varphi(n)=\sum_{x=1}^{n}[gcd(x,n)=1]\)

性质

性质一

\(p\) 是质数,则 \(\varphi(p)=p-1\)

证明:

\(p\) 是质数,则 \(p\) 仅有 \(p\)\(1\) 两个正因子

\(1\sim p\) 中 ,只有 \(p\) 含有正因子 \(p\),因此 \(\varphi(p)=p-1\)

性质二

\(p\) 是质数,则 \(\varphi(p^k)=(p-1)\times p^{k-1}\)

证明(非严谨):

\(p\) 是质数,则在 \(1\sim p^k\) 中,仅有 \(p,2p,3p,\cdots,p^{k-1}\times p\) 含因子 \(p\)

因此,\(\varphi(p^k)=p^k-p^{k-1}=(p-1)\times p^{k-1}\)

性质三

欧拉函数是一个积性函数

即 若\(gcd(m,n)=1\),则 \(\varphi(mn)=\varphi(m)\varphi(n)\)

证明略.

计算公式

\(\varphi(n)=n\times \prod_{i=1}^{s}(1-\dfrac{1}{p_i})\)

注:欧拉函数仅由 \(n\) 和其质因子决定,与质因子次数无关

证明(用到以上三个性质及算术基本定理):

由算术基本定理 \(n=\prod_{i=1}^{S} p_{i}^{\alpha_{i}}=p_{1}^{\alpha_{1}} p_{2}^{\alpha_{2}} \cdots p_{s}^{\alpha_{s}}\)

\[\begin{aligned} \varphi(n) & =\prod_{i=1}^{S} \varphi\left(p_{i}^{\alpha_{i}}\right) \\ & =\prod_{i=1}^{s} p_{i}^{\alpha_{i}-1}\left(p_{i}-1\right) \\ & =\prod_{i=1}^{S} p_{i}^{\alpha_{i}}\left(1-\frac{1}{p_{i}}\right) \\ & =\prod_{i=1}^{S} p_{i}^{\alpha_{i}} \times \prod_{i=1}^{S}\left(1-\frac{1}{p_{i}}\right) \\ & =n \times \prod_{i=1}^{S} \frac{p_{i}-1}{p_{i}} \end{aligned}\]

求单个数的欧拉函数

根据计算公式,通过分解质因子求欧拉函数

static int getPhi(int n) {
    int ans = n;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            ans = ans / i * (i - 1);
            do n /= i; while (n % i == 0);
        }
    }
    if (n > 1) ans = ans / n * (n - 1);
    return ans;
}

筛欧拉函数

线性筛质数的时候,我们提到每个合数 \(m\) 都是被其最小的质因子筛去的。

\(p_j\) 是当前遍历到的质数,则 \(m\) 通过 \(m=p_j\times i\) 筛去

  1. \(i\) 能被 \(p_j\) 整除,则 \(p_j\)\(i\)\(m\) 的最小质因子,且 \(i\) 包含了 \(m\) 的所有质因子
    \(\varphi(m)=m\times\prod_{k=1}^{s}\dfrac{p_k-1}{p_k}=p_j\times i\times\prod_{k=1}^{s}\dfrac{p_k-1}{p_k}=p_j\times\varphi(i)\)
  2. \(i\) 不能被 \(p_j\) 整除,则 \(i\)\(p_j\) 是互质的
    \(\varphi(m)=\varphi(p_j\times i)=\varphi(p_j)\times\varphi(i)=(p_j-1)\times\varphi(i)\)
static int[] primes, phi;
static boolean[] isPrime;
// 求1~n的欧拉函数
static int getPhi(int n) {
    primes = new int[(n + 1) / 2];
    phi = new int[n + 1];
    isPrime = new boolean[n + 1];
    int tot = 0;
    for (int i = 2; i <= n; ++i) isPrime[i] = true;
    phi[1] = 1;//1单独处理
    for (int i = 2; i <= n; ++i) {
        //如果i是质数
        if (isPrime[i]) {
            primes[tot++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[m] = false;
            //如果i能被primes[j]整除,则i包含了合数i*prime[j]的所有质因子
            if (i % primes[j] == 0) {
                phi[m] = primes[j] * phi[i];
                break;
            }
            //否则i与primes[j]是互质的
            phi[m] = phi[primes[j]] * phi[i];
            //也可以这么写
            //phi[m]] = (primes[j] - 1) * phi[i];
        }
    }
    return tot;
}

约数(正因子)个数

定义

正整数 \(n\) 的约数(正因子)个数 \(d(n)\)\(\sigma_0(n)\)\(\sigma_0(n)=\sum_{d \mid n} 1\)

计算公式

约数个数定理 - 百度百科

对于正整数 \(n\) ,由算术基本定理得:\(n=\prod_{i=1}^{S} p_{i}^{\alpha_{i}}\),则 \(\sigma_0(n)=\prod_{i=1}^{S}(\alpha_{i}+1)\)

注:约数个数只由其质因子次数决定

证明:

\(p_i^{\alpha_i}\) 的约数有 \(p_i^0,p_i^1,\cdots,p_i^{\alpha_i}\)\((\alpha_i+1)\)

由乘法原理(分步乘法)得:\(\sigma_0(n)=\prod_{i=1}^{S}(\alpha_{i}+1)\)

求单个数的约数个数

根据计算公式,通过分解质因子求约数个数

static int getSigma0(int n) {
    int ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int k = 1;
            while (n % i == 0) {
                ++k;
                n /= i;
            }
            ans *= k;
        }
    }
    if (n > 1) ans *= 2;
    return ans;
}

筛约数个数

记录最小质因子次数

定义 \(\sigma_0[i]\) 表示 \(i\) 的约数个数,\(Kth[i]\) 表示 \(i\) 的最小质因子的次数(指数)

\(i\) 是质数,则 \(Kth[i]=1\)\(\sigma_0[i]=2\)

\(p_j\) 是当前遍历到的质数,则 \(m\) 通过 \(m=p_j\times i\) 筛去

  1. \(i\) 能被 \(p_j\) 整除,则 \(p_j\) 一定是 \(i\)\(m\) 的最小质因子,此时 \(i\)\(m\) 的差别仅为最小质因子次数多 \(1\)

    \(\sigma_0[i]=(\alpha_1+1)\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)\)

    \(\sigma_0[m]=(\alpha_1+1+1)\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)\)

    • \(Kth[m]=Kth[i]+1\)
    • \(\sigma_0[m]=\sigma_0[i]\div (Kth[i]+1)\times (Kth[m]+1)=\sigma_0[i]\div (Kth[i]+1)\times (Kth[i]+2)\)
  2. \(i\) 不能被 \(p_j\) 整除,则 \(i\) 不含质因子 \(p_j\),换言之,\(m\) 的最小质因子 \(p_j\) 的指数为 \(1\)
    • \(Kth[m]=1\)
    • \(\sigma_0[m]=\sigma_0[i]\times(1+1)=2\times\sigma_0[i]\)
static int[] primes, sigma0, Kth;
static boolean[] isPrime;
// 求1~n的约数个数
static int getSigma0(int n) {
    primes = new int[(n + 1) / 2];
    sigma0 = new int[n + 1];
    Kth = new int[n + 1];
    isPrime = new boolean[n + 1];
    int tot = 0;
    for (int i = 2; i <= n; ++i) isPrime[i] = true;
    sigma0[1] = 1;//1单独处理
    for (int i = 2; i <= n; ++i) {
        //如果i是质数
        if (isPrime[i]) {
            primes[tot++] = i;
            Kth[i] = 1;
            sigma0[i] = 2;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[m] = false;
            //如果i能被primes[j]整除,则i包含了合数m的所有质因子
            if (i % primes[j] == 0) {
                Kth[m] = Kth[i] + 1;
                sigma0[m] = sigma0[i] / (Kth[i] + 1) * (Kth[i] + 2);
                break;
            }
            //否则i不含质因子primes[j]
            Kth[m] = 1;
            sigma0[m] = 2 * sigma0[i];
            //也可以这么写
            //sigma0[m] = sigma0[primes[j]] * sigma0[i];
        }
    }
    return tot;
}

不记录最小质因子次数

\(Kth[i]\) 最小质因子次数只用于上述情况一求 \(\sigma_0[m]\)

如果不通过 \(Kth[i]\)\(\sigma_0[m]\) ,则可以大大减少运算次数以及所需空间

上述情况一( \(i\) 能被 \(p_j\) 整除):

\(\begin{aligned} \sigma_0[m]&=(\alpha_1+1+1)\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)\\ &=2\times(\alpha_1+1)\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)-\alpha_1\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)\\ &=2\times\sigma_0[i]-\alpha_1\times(\alpha_2+1)\times\cdots\times(\alpha_s+1)\\ &=2\times\sigma_0[i]-\sigma_0[\dfrac{i}{p_j}] \end{aligned}\)

static int[] primes, sigma0;
static boolean[] isPrime;
// 求1~n的约数个数
static int getSigma0(int n) {
    primes = new int[(n + 1) / 2];
    sigma0 = new int[n + 1];
    isPrime = new boolean[n + 1];
    int tot = 0;
    for (int i = 2; i <= n; ++i) isPrime[i] = true;
    sigma0[1] = 1;//1单独处理
    for (int i = 2; i <= n; ++i) {
        //如果i是质数
        if (isPrime[i]) {
            primes[tot++] = i;
            sigma0[i] = 2;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[i * primes[j]] = false;
            //如果i能被primes[j]整除,则i包含了合数m的所有质因子
            if (i % primes[j] == 0) {
                sigma0[m] = 2 * sigma0[i] - sigma0[i / primes[j]];
                break;
            }
            //否则i与不含质因子primes[j]
            sigma0[m] = 2 * sigma0[i];
            //也可以这么写
            //sigma0[m] = sigma0[primes[j]] * sigma0[i];
        }
    }
    return tot;
}

约数(正因子)之和

定义

正整数 \(n\) 的所有约数(正因子)之和 \(\sigma_1(n)\)\(\sigma_1(n)=\sum_{d \mid n} d\)

计算公式

约数和定理 - 百度百科

对于正整数 \(n\) ,由算术基本定理得:\(n=\prod_{i=1}^{S} p_{i}^{\alpha_{i}}\),则 \(\sigma_1(n)=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i}p_i^j\)

求和部分可以使用等比数列求和公式计算

注:约数和只由其质因子及其质因子次数决定

证明:

\(p_i^{\alpha_i}\) 的约数有 \(p_i^0,p_i^1,\cdots,p_i^{\alpha_i}\)

\(p_i^{\alpha_i}\) 的约数和为 \(\sum_{j=0}^{\alpha_i}p_i^j\)

由乘法原理(分步乘法)得:\(\sigma_1(n)=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i}p_i^j\)

例:\(12=2^2\times3^1\)\(\sigma_1(12)=(1+2+4)\times(1+3)=7\times 4=28\)

求单个数的约数和

根据计算公式,通过分解质因子求约数和

static int getSigma1(int n) {
    int ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int sum = 1, p = 1;
            while (n % i == 0) {
                p *= i;
                sum += p;
                n /= i;
            }
            ans *= sum;
        }
    }
    if (n > 1) ans *= 1 + n;
    return ans;
}

一个数的幂的约数和(暂未经过数据测试)

97. 约数之和 - AcWing题库

即求 \(\sigma_1(n^k)\)

对于正整数 \(n\) ,由算术基本定理得:\(n=\prod_{i=1}^{S} p_{i}^{\alpha_{i}}\),则 \(\sigma_1(n^k)=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i\times k}p_i^j\)

证明:

\[\begin{aligned} \sigma_1(n^k)&=\sigma_1((\prod_{i=1}^{S} p_{i}^{\alpha_{i}})^k)\\ &=\prod_{i=1}^{S}\sigma_1(p_i^{\alpha_i\times k})\\ &=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i\times k}p_i^j \end{aligned} \]

static int getSigma1(int n, int k) {
    int ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int index = 0;
            while (n % i == 0) {
                n /= i;
                index += k;
            }
            int sum = 1;
            int p = 1;
            for (int j = 0; j < index; ++j) {
                p *= i;
                sum += p;
            }
            ans *= sum;
        }
    }
    if (n > 1) {
        int sum = 1;
        int p = 1;
        for (int i = 0; i < k; ++i) {
            p *= n;
            sum += p;
        }
        ans *= sum;
    }
    return ans;
}

求和部分可以使用等比数列求和公式 \(S_n=a_1\times\dfrac{1-q^n}{1-q}\),则

\[\sigma_1(n^k)=\prod_{i=1}^{S}(\dfrac{p_i^{\alpha_i\times k+1}-1}{p_i-1}) \]

求幂部分可以使用快速幂加速求解

static int pow(int a, int b) {
    int ret = 1;
    while (b != 0) {
        if ((b & 1) == 1) ret *= a;
        b >>= 1;
        a *= a;
    }
    return ret;
}
static int getSigma1(int n, int k) {
    int ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            int index = 1;
            while (n % i == 0) {
                n /= i;
                index += k;
            }
            int sum = (pow(i, index) - 1) / (i - 1);
            ans *= sum;
        }
    }
    if (n > 1) {
        int sum = (pow(n, 1 + k) - 1) / (n - 1);
        ans *= sum;
    }
    return ans;
}

如果使用等比数列求和公式且要取模,则需要求解逆元

筛的约数之和

记录最小质因子的幂的和式

定义 \(g[i]\) 表示 \(i\) 的最小质因子的幂的和式 ,即\(g[i]=\sum_{k=0}^{\alpha_i}p_j^k\)

\(\sigma_1[i]\) 表示 \(i\) 的约数和

\(i\) 是质数,\(g[i]=\sigma_1[i]=i+1\)

\(p_j\) 是当前遍历到的质数,则 \(m\) 通过 \(m=p_j\times i\) 筛去

  1. \(i\) 能被 \(p_j\) 整除,则 \(p_j\)\(i\)\(m\) 的最小质因子,此时 \(m\)\(m\) 的差别仅为最小质因子 \(p_j\) 的次数多 \(1\)

    \(g[i]=p_j^0+p_j^1+\cdots+p_j^{\alpha_j}\)

    \(g[m]=p_j^0+p_j^1+\cdots+p_j^{\alpha_j}+p_j^{\alpha_j+1}\)

    \(\sigma_1[i]=g[i]\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j\)

    \(\sigma_1[m]=g[m]\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j\)

    • \(g[m]=p_j^0+p_j^1+\cdots+p_j^{\alpha_j}+p_j^{\alpha_j+1}=1+p_j\times(p_j^0+p_j^1+\cdots+p_j^{\alpha_j})=1+p_j\times g[i]\)
    • \(\sigma_1[m]=g[m]\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j=g[m]\times\dfrac{\sigma_1[i]}{g[i]}\)
  2. \(i\) 不能被 \(p_j\) 整除,则 \(i\) 不含质因子 \(p_j\),换言之,\(m\) 的最小质因子 \(p_j\) 的指数为 \(1\)
    • \(g[m]=1+p_j\)
    • \(\sigma_1[m]=g[m]\times\sigma_1[i]\)
static int[] primes, sigma1, g;
static boolean[] isPrime;
// 求1~n的约数和
static int getSigma1(int n) {
    primes = new int[(n + 1) / 2];
    sigma1 = new int[n + 1];
    g = new int[n + 1];
    isPrime = new boolean[n + 1];
    int tot = 0;
    for (int i = 2; i <= n; ++i) isPrime[i] = true;
    sigma1[1] = 1;//1单独处理
    for (int i = 2; i <= n; ++i) {
        //如果i是质数
        if (isPrime[i]) {
            primes[tot++] = i;
            g[i] = sigma1[i] = i + 1;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[m] = false;
            //如果i能被primes[j]整除,则i包含了合数i*prime[j]的所有质因子
            if (i % primes[j] == 0) {
                g[m] = 1 + primes[j] * g[i];
                sigma1[m] = sigma1[i] / g[i] * g[m];
                break;
            }
            //否则i不含质因子primes[j],即m的最小质因子prime[j]的指数为1
            g[m] = 1 + primes[j];
            sigma1[m] = g[m] * sigma1[i];
            //也可以这么写
            //sigma1[m] = sigma1[primes[j]] * sigma1[i];
        }
    }
    return tot;
}

不记录最小质因子的幂的和式

如果不通过 \(g[i]\)\(\sigma_1[m]\) ,则可以大大减少运算次数以及所需空间

上述情况一( \(i\) 能被 \(p_j\) 整除):

考虑 \(\sigma_1(i)、\sigma_1(\dfrac{i}{p_j})\)\(\sigma_1(m)\) 的关系

\(T=\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j\),则

\[\begin{aligned} \sigma_1[i]&=(p_j^0+p_j^1+\cdots+p_j^{\alpha_j})\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j=(p_j^0+p_j^1+\cdots+p_j^{\alpha_j})\times T&(1)\\ \sigma_1[m]&=(p_j^0+p_j^1+\cdots+p_j^{\alpha_j+1})\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j=\sigma_1[i]+p_j^{\alpha_j+1}\times T&(2)\\ \sigma_1[\dfrac{i}{p_j}]&=(p_j^0+p_j^1+\cdots+p_j^{\alpha_j-1})\times\prod_{k=2}^{S}\sum_{j=0}^{\alpha_k}p_k^j=\sigma_1[i]-p_j^{\alpha_j}\times T&(3)\\ \end{aligned}\]

\(p_j\times\left(3\right) +\left(2\right)\),得:

\[\sigma_1[m]+p_{j}\sigma_1[\dfrac{i}{p_{1}}]=\left(p_{j}+1\right)\times\sigma_1[i] \]

整理,得

\[\sigma_1[m]=(p_j+1)\times\sigma_1[i]-p_j\times\sigma_1[\dfrac{i}{p_j}] \]

static int[] primes, sigma1;
static boolean[] isPrime;
// 求1~n的约数和
static int getSigma1(int n) {
    primes = new int[(n + 1) / 2];
    sigma1 = new int[n + 1];
    isPrime = new boolean[n + 1];
    int tot = 0;
    for (int i = 2; i <= n; ++i) isPrime[i] = true;
    sigma1[1] = 1;//1单独处理
    for (int i = 2; i <= n; ++i) {
        //如果i是质数
        if (isPrime[i]) {
            primes[tot++] = i;
            sigma1[i] = i + 1;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[m] = false;
            //如果i能被primes[j]整除,则i包含了合数i*prime[j]的所有质因子
            if (i % primes[j] == 0) {
                sigma1[m] = (1 + primes[j]) * sigma1[i] - primes[j] * sigma1[i / primes[j]];
                break;
            }
            //否则i不含质因子primes[j],即m的最小质因子prime[j]的指数为1
            sigma1[m] = (1 + primes[j]) * sigma1[i];
            //也可以这么写
            //sigma1[m] = sigma1[primes[j]] * sigma1[i];
        }
    }
    return tot;
}

求连续区间的数的约数和的和

即求 \(\sum_{i=l}^{r}\sigma_1(i)\)

可不通过筛法求解,而通过数论分块(整除分块)的方法在 \(O(\sqrt{r-l+1})\) 的时间复杂度下求解

具体解释见 洛谷 P1403 约数研究 - Cattle_Horse

import java.util.Scanner;

public class Main {
    //计算 1~n 所有约数的和
    static long calculate(long n) {
        long ans = 0;
        for (long l = 1, r; l <= n; l = r + 1) {
            r = Math.min(n / (n / l), n);
            ans += (l + r) * (r - l + 1) / 2 * (n / l);
        }
        return ans;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt(), y = sc.nextInt();
        System.out.println(calculate(y) - calculate(x - 1));
    }
}

约数(正因子)k次幂之和

定义

正整数 \(n\) 的所有约数(正因子)\(k\) 次幂之和 \(\sigma_k(n)\)\(\sigma_k(n)=\sum_{d \mid n} d^k\)

计算公式

对于正整数 \(n\) ,由算术基本定理得:\(n=\prod_{i=1}^{S} p_{i}^{\alpha_{i}}\),则 \(\sigma_k(n)=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i}p_i^{j\times k}\)

证明:

对于质数 \(p\)\(p^{\alpha}\) 的约数有 \(p^0,p^1,\cdots,p^\alpha\),则 \(\sigma_k(p^{\alpha})=\sum_{i=0}^{\alpha}p^{i\times k}\)

又由算术基本定理及积性函数的性质得:

\[\begin{aligned} \sigma_k(n)&=\sigma_k(\prod_{i=1}^{S} p_{i}^{\alpha_{i}})\\ &=\prod_{i=1}^{S}\sigma_k(p_i^{\alpha_{i}})\\ &=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i}p_i^{j\times k} \end{aligned} \]

求单个数的约数(正因子)k次幂之和

根据计算公式,通过分解质因子求约数(正因子)\(k\) 次幂之和

幂次部分可以使用快速幂求解

static final int MOD = (int) 1e9 + 7;
static int pow(int a, int b) {
    long ans = 1;
    while (b != 0) {
        if (b % 2 == 1) ans = ans * a % MOD;
        a = a * a % MOD;
        b /= 2;
    }
    return (int) ans;
}
static int getSigmaK(int n, int k) {
    int ans = 1;
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            long sum = 1;
            int index = 0;
            while (n % i == 0) {
                n /= i;
                index += k;
                sum = (sum + pow(i, index)) % MOD;
            }
            ans = (int) ((long) ans * sum % MOD);
        }
    }
    if (n > 1) {
        long sum = (pow(n, k) + 1) % MOD;
        ans = (int) (ans * sum % MOD);
    }
    return ans;
}

筛约数(正因子)k次幂之和

\[\sigma_k(n)=\prod_{i=1}^{S}\sum_{j=0}^{\alpha_i}p_i^{j\times k} \]

定义 \(i\) 为当前遍历到的数

\(i\) 为质数,则 \(\sigma_k(i)=\sum_{j=0}^{1}i^{j\times k}=i^{k}+1\)

\(p_j\) 是当前遍历到的质数,\(m=i\cdot p_j\)

  1. \(p_j\) 能整除 \(i\),则 \(p_j\)\(i\)\(m\) 的最小质因子,即 \(m\) 仅是最小质因子 \(p_j\) 的指数比 \(i\)\(1\)

    考虑 \(\sigma_k(i)、\sigma_k(\dfrac{i}{p_j})\)\(\sigma_k(m)\) 的关系

    \[\begin{aligned} 令\ T&=\prod_{x=2}^{S}\sum_{y=0}^{\alpha_x}p_x^{y\times k},则\\ \sigma_k(i)&=(\sum_{y=0}^{\alpha_1}p_j^{y\times k})\times(\prod_{x=2}^{S}\sum_{y=0}^{\alpha_x}p_x^{y\times k})=T\times\sum_{y=0}^{\alpha_1}p_j^{y\times k}&(1)\\ \sigma_k(\dfrac{i}{p_j})&=(\sum_{y=0}^{\alpha_1-1}p_j^{y\times k})\times(\prod_{x=2}^{S}\sum_{y=0}^{\alpha_x}p_x^{y\times k})=\sigma_k(i)-T\times p_j^{\alpha_1\times k}&(2)\\ \sigma_k(m)&=(\sum_{y=0}^{\alpha_1+1}p_j^{y\times k})\times(\prod_{x=2}^{S}\sum_{y=0}^{\alpha_x}p_x^{y\times k})=\sigma_k(i)+T\times p_j^{\alpha_1\times k}\times p_j^{k}&(3)\\ \end{aligned} \]

    \(p_j^k\times(2)+(3)\) 得 :\(\sigma_k(\dfrac{i}{p_j})\times p_j^k+\sigma_k(m)=(p_j^k+1)\times\sigma_k(i)\)

    整理,得:\(\sigma_k(m)=(p_j^k+1)\times\sigma_k(i)-\sigma_k(\dfrac{i}{p_j})\times p_j^k\)

    因此需要预处理出 \(p_j^k\) 的值,可以使用快速幂求得

  2. \(p_j\) 不能整除 \(i\),则 \(p_j\)\(i\) 互质,换言之,\(m\) 的最小质因子 \(p_j\) 的指数为 \(1\)

    \(\sigma_k(m)=\sigma_k(i)\times (p_j^k+1)=\sigma_k(i)\times\sigma_k(p_j)\)

    PS:所以它是积性函数

static long pow(long a, int b) {
    long ans = 1L;
    while (b != 0) {
        if (b % 2 == 1) ans = ans * a;
        a = a * a;
        b >>= 1;
    }
    return ans;
}
static int[] primes;
static boolean[] isPrime;
static long[] pk;
static long[] sigmaK;
// 求1~n的约数(正因子)k次幂之和
public static int getSigmaK(final int n, final int k) {
    int len = (int) Math.max((n + 1) / (Math.log(n + 1) - 1.112), 1);
    primes = new int[len];
    isPrime = new boolean[n + 1];
    Arrays.fill(isPrime, true);
    isPrime[0] = isPrime[1] = false;
    pk = new long[n + 1];
    sigmaK = new long[n + 1];
    sigmaK[1] = pk[1] = 1;//1单独处理
    int tot = 0;
    for (int i = 2; i <= n; ++i) {
        if (isPrime[i]) {
            primes[tot++] = i;
            pk[i] = pow(i, k);
            sigmaK[i] = pk[i] + 1;
        }
        for (int j = 0, m; j < tot && (m = i * primes[j]) <= n; ++j) {
            isPrime[m] = false;
            pk[m] = pk[primes[j]];
            if (i % primes[j] == 0) {
                sigmaK[m] = (pk[primes[j]] + 1) * sigmaK[i] - sigmaK[i / primes[j]] * pk[i];
                break;
            }
            sigmaK[m] = sigmaK[i] * (pk[primes[j]] + 1);
            // 也可以这么写
            // sigmaK[m] = sigmaK[i] * sigmaK[primes[j]];
        }
    }
    return tot;
}

参考资料

数论学习笔记1之积性函数与线性筛求积性函数_cqust_qilin02811的博客

积性函数与筛法 - 又又大柚纸

515 筛法求欧拉函数 - 董晓算法

516 筛法求约数个数 - 董晓算法

517 筛法求约数和 - 董晓算法

线性筛约数个数、约数和的新方法 - ldysy2102

约数之和 (数论) - 基地AI

浅谈一类积性函数的前缀和_skywalkert的博客

posted @ 2023-01-08 18:57  Cattle_Horse  阅读(75)  评论(0编辑  收藏  举报