积性函数及其筛法
积性函数及其筛法
前置知识
一些定理
互质:公约数只有 \(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\) 筛去
- 若 \(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)\) - 若 \(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\) 筛去
- 若 \(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)\)
- 若 \(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;
}
一个数的幂的约数和(暂未经过数据测试)
即求 \(\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}\),则
求幂部分可以使用快速幂加速求解
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\) 筛去
- 若 \(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]}\)
- 若 \(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次幂之和
定义 \(i\) 为当前遍历到的数
若 \(i\) 为质数,则 \(\sigma_k(i)=\sum_{j=0}^{1}i^{j\times k}=i^{k}+1\)
设 \(p_j\) 是当前遍历到的质数,\(m=i\cdot p_j\)
-
若 \(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\) 的值,可以使用快速幂求得
-
若 \(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;
}