算术基本定理
一个整数可以被表示成若干质数的乘积。例如:\(48=2^4 \times 3, \ 49 = 7^2, \ 50 = 2 \times 5^2\)。
算术基本定理:设 \(a>1\),那么必有 \(a = p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_s^{\alpha_s}\),其中 \(p_i \ (1 \le i \le s)\) 是两两不相同的质数,\(\alpha_i \ (1 \le i \le s)\) 表示对应质数的幂次。
朴素的分解质因数的时间复杂度和枚举约数一样都是 \(O(\sqrt{n})\)。
int decomposition(int x) {
// 分解x,数组a记录所有质数,返回分解出来的质数数量
int cnt = 0;
for (int i = 2; i <= x / i; i++) {
while (x % i == 0) {
a[++cnt] = i;
x /= i;
}
}
if (x > 1) a[++cnt] = x;
return cnt;
}
算术基本定理是处理整除性和数论函数的有力工具。
假定 \(a = p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_s^{\alpha_s}\),有以下推论:
推论:\(d\) 是 \(a\) 的约数的充要条件是 \(d = p_1^{e_1} p_2^{e_2} \cdots p_s^{e_s}, \ 0 \le e_i \le \alpha_i, \ 1 \le i \le s\),即 \(d\) 中每个质数的幂次都不超过 \(a\)。
每个质因子上的幂次直接决定了两数之间的整除性。
例如 \(12 = 2^2 \times 3, \ 72 = 2^3 \times 3^2\),\(12\) 的每个质因子上的幂次都对应地不超过 \(72\) 的,因此 \(12 | 72\)。
推论:若 \(b = p_1^{\beta_1} p_2^{\beta_2} \cdots p_s^{\beta_s}\),这里允许某些 \(\alpha_i\) 或 \(\beta_i\) 为零,那么 \((a,b) = p_1^{\delta_1} p_2^{\delta_2} \cdots p_s^{\delta_s}, \ \delta_i = \min (\alpha_i, \beta_i), \ 1 \le i \le s\),以及 \([a,b] = p_1^{\gamma_1} p_2^{\gamma_2} \cdots p_s^{\gamma_s}, \ \gamma_i = \max(\alpha_i, \beta_i), \ 1 \le i \le s\)。
比如,\(10 = 2 \times 5, \ 16 = 2^4\),那么 \((10,16) = 2^{\min(1,4)} \times 5^{\min(1,0)} = 2^1 \times 5^0 = 2\),且 \([10,16] = 2^{\max(1,4)} \times 5^{\max(1,0)} = 2^4 \times 5^0 = 80\)。
另外,这个性质也是 \([a_1,a_2](a_1,a_2) = a_1a_2\) 的一种直接证明,以 \(10\) 和 \(16\) 为例:\((10,16)[10,16] = 2^{\min(1,4)} \times 5^{\min(1,0)} \times 2^{\max(1,4)} \times 5^{\max(1,0)} = 2^{\min(1,4) + \max(1,4)} \times 5^{\min(1,0) + \max(1,0)} = 2^{1+4} \times 5^{1+0}\),\(10 \times 16 = 2 \times 5 \times 2^4 = 2^{1+4} \times 5^{1+0}\)。
所以 \([a_1,a_2](a_1,a_2) = a_1a_2\) 的本质其实是 \(\min(\alpha,\beta) + \max(\alpha,\beta) = \alpha + \beta\)。
推论:用除数函数 \(\tau(a)\) 表示 \(a\) 的所有正约数的个数,则 \(\tau(a) = (\alpha_1+1) \cdots (\alpha_s + 1) = \tau(p_1^{\alpha_1}) \cdots \tau(p_s^{\alpha_s})\)。
相当于对于每个质因子上的幂次,可以取 \(0\) 到 \(\alpha_i\) 中的任意整数,共 \(\alpha_i + 1\) 个,由乘法原理可以直接得出,约数的个数。
比如,\(a = 2^7 \times 3^8 \times 5^9\),则 \(a\) 的正约数个数等于 \((7+1)(8+1)(9+1) = 8 \times 9 \times 10 = 720\)。
推论:用除数和函数 \(\sigma(a)\) 表示 \(a\) 的所有正约数的和,则 \(\sigma(a) = \dfrac{p_1^{\alpha_1 + 1} - 1}{p_1 - 1} \cdots \dfrac{p_s^{\alpha_s + 1} - 1}{p_s - 1} = \sigma(p_1^{\alpha_1}) \cdots \sigma(p_s^{\alpha_s})\)。
比如,\(a = 120 = 2^3 \times 3 \times 5\) 的因子分别是 \(1,2,3,4,5,6,8,10,12,15,20,24,30,40,60,120\)。
用等比数列求和公式 \(\dfrac{p_1^{\alpha_1 + 1} - 1}{p_1 - 1} = 1 + p_1 + \cdots + p_1^{\alpha_1}\) 展开计算 \(\sigma(120) = \dfrac{2^4 - 1}{2 - 1} \dfrac{3^2-1}{3-1} \dfrac{5^2-1}{5-1} = (2^3+2^2+2^1+1)(3^1+1)(5^1+1)\),把括号展开,发现 \(\sigma(120) = (2^3+2^2+2^1+1)(3^1+1)(5^1+1) = 120+24+40+8+60+12+20+4+30+6+10+2+15+3+5+1\),等于之前的约数之和。
公式是乘法原理在乘法分配律上的体现,也展现了质因子之间的独立性。
例题:P1069 [NOIP2009 普及组] 细胞分裂
给定 \(m_1 \ (m_1 \le 30000), \ m_2 \ (m_2 \le 10000)\) 和 \(n \ (n \le 10000)\) 个正整数 \(S_i \ (S_i \le 2 \times 10^9)\)。设 \(x_i\) 是最小的使得 \(m_1^{m_2}\) 整除 \(S_i^{x_i}\) 的整数(也有可能不存在),求 \(\min(x_1, \cdots, x_n)\)。
分析:\(m_1^{m_2}\) 很大,难以直接处理。考虑 \(m_1 = p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_s^{\alpha_s}\),那么 \(m_1^{m_2} = p_1^{\alpha_1 m_2} p_2^{\alpha_2 m_2} \cdots p_s^{\alpha_s m_2}\)。接下来只需要解决题意中的 \(x_i\) 的求解。
当 \(m_1^{m_2}\) 中的每个质因子的幂次都不超过 \(S_i^{x_i}\) 时,\(m_1^{m_2} | S_i^{x_i}\) 成立。\(S_i^{x_i}\) 中的质因子是否出现由 \(S_i\) 决定,而质因子出现了几次主要由 \(x_i\) 决定。若 \(S_i = p_1^{e_1} p_2^{e_2} \cdots p_s^{e_s} p_{s+1}^{e_{s+1}} \cdots p_{s+r}^{e_{s+r}}\),其中 \(p_{s+1}\) 到 \(p_{s+r}\) 表示与 \(m_1\) 无关的质因子,那么 \(x_i\) 应该使得对于所有 \(1 \le j \le s\),满足 \(\alpha_j m_2 \le e_j x_i\)。
所以从 \(m_1\) 的每个质因子 \(p_j\) 出发:如果这个 \(p_j\) 不能整除 \(S_i\),则说明 \(S_i\) 不包含这个质因子,进而说明找不到题设要求的 \(x_i\);如果这个 \(S_i\) 能整除 \(p_j\),那么只要求出对应的 \(e_j\),就能算出第 \(j\) 个质因子,要求 \(x_i\) 不小于 \(\left\lceil \dfrac{\alpha_j m_2}{e_j} \right\rceil\),再对所有这样的要求取最大值,就得到了 \(x_i\)。最后对所有合法的 \(x_i\) 取最小值就是答案。
参考代码
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 200;
int cnt[N], cnts[N];
int main()
{
int n, m1, m2;
scanf("%d%d%d", &n, &m1, &m2);
int m = m1;
// 对m1分解质因数
for (int i = 2; i * i <= m; i++) {
while (m1 % i == 0) {
cnt[i]++;
m1 /= i;
}
}
int ans = -1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < N; j++) cnts[j] = 0;
int s;
scanf("%d", &s);
// 对s分解质因数
for (int j = 2; j * j <= m; j++) {
while (s % j == 0) {
cnts[j]++;
s /= j;
}
}
bool ok = true;
int tmp = 0;
if (m1 > 1) {
int cntm = 0;
while (s % m1 == 0) {
cntm++; s /= m1;
}
if (cntm == 0) continue;
tmp = (m2 + cntm - 1) / cntm;
}
for (int j = 2; j * j <= m; j++) {
if (cnt[j] != 0 && cnts[j] == 0) {
ok = false; break;
}
if (cnt[j] > 0 && cnts[j] > 0) {
tmp = max(tmp, (cnt[j] * m2 + cnts[j] - 1) / cnts[j]);
}
}
if (ok && (ans == -1 || tmp < ans)) ans = tmp;
}
printf("%d\n", ans);
return 0;
}
习题:P9836 种树
解题思路
假设树高 \(p\) 分解质因数之后是 \(p_1^{\alpha_1} p_2^{\alpha_2} \cdots p_s^{\alpha_s}\),则它的正因数个数是 \((\alpha_1 + 1)(\alpha_2 + 1) \cdots (\alpha_s + 1)\)。
每次施肥实际上是考虑肥料 \(w\) 的每一个质因子该用在哪棵树上,这就相当于把该树高度的这个质因子的指数 \(+1\),假设原来的指数是 \(x\),则施肥后指数变为 \(x+1\),而指数是 \(x\) 时对正因子个数的贡献是 \(x+1\),指数是 \(x+1\) 时对正因子个数的贡献是 \(x+2\),因此整体的正因子个数乘积变为原来的 \(\dfrac{x+2}{x+1}\)。
分析这个式子可以发现,原来的 \(x\) 越小,对其施肥后对整体的贡献增长最大,所以应该把这个质因子用在该质因子指数最小的那棵树上,按照这个策略把肥料中的每个质因子使用出去即可。
时间复杂度为 \(O(nm)\),这里的 \(m\) 指的是 \(n\) 以内的质数个数(\(\le 1500\))。
参考代码
#include <cstdio>
const int N = 10005;
const int M = 1500;
const int MOD = 998244353;
int cnt[N][M], prime[N], len;
bool is_prime[N];
void init() {
for (int i = 2; i < N; i++) is_prime[i] = true;
for (int i = 2; i < N; i++) {
if (is_prime[i]) {
prime[++len] = i;
for (int j = i * 2; j < N; j += i) is_prime[j] = false;
}
}
}
int main()
{
init();
int n, w;
scanf("%d%d", &n, &w);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
for (int j = 1; j <= len; j++) {
cnt[i][j] = 1;
while (x % prime[j] == 0) {
cnt[i][j]++;
x /= prime[j];
}
}
}
for (int i = 1; i <= len; i++) {
while (w % prime[i] == 0) {
int minj = 1;
for (int j = 2; j <= n; j++)
if (cnt[j][i] < cnt[minj][i]) minj = j;
cnt[minj][i]++;
w /= prime[i];
}
}
int ans = 1;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= len; j++)
ans = 1ll * ans * cnt[i][j] % MOD;
printf("%d\n", ans);
return 0;
}