Reference
- OI Wiki 数论部分
- 《初等数论》 - 潘承洞、潘承彪(重点看第一章)
A. XTUOJ 1121 欧拉函数
分解质因数,模拟
- 利用分解质因数得到 n n n 的所有质因子再将其带入欧拉函数的计算公式即可
- 分解质因数的朴素算法如下
for (int i = 2; i * i <= N; ++i) { if (N % i == 0) { // 如果 i 能整除 N,说明 i 是 N 的一个质因子 res[size++] = i; while (N % i) N /= i; } } if (N != 1) res[size++] = N; // 最后若 N 不等于 1,说明留下了一个素数
- 下面我们来证明上面算法的正确性:
我们用 N N N 来表示还进行质因数分解的初始值,N
表示代码执行过程中的 N N N
➀ 首先考察N
的变化。当for
循环进行到某个i
(开始时有i
整除N
)结束时,必然已经执行完了while (N % i) N/= i;
,因此此时i
不能整除N
,但N
整除 N N N 仍成立。由此我们有:当循环进行到i
开始时,N
整除 N N N 且不被任何小于i
的整数(不考虑 1 1 1)整除。
② 其次证明res
内的元素都为 N N N 的因子。for
循环内i
加入res
的条件是N % i == 0;
,因此for
循环内纳入的i
必然都是 N N N 的因子,对于for
循环结束后得到的N
,如果它不为 1 1 1,由 ➀ 可知它必然是 N N N 的因子。
③ 然后证明res
内的元素都是素数。假设res
内存在一个合数 K K K,必然有 k ≤ K k \le \sqrt{K} k≤K 满足 k k k 整除 K K K ,这样的 K K K 不可能做为某个i
被加入res
:当循环进行到 K K K 时, k k k 整除 K \sqrt{K} K 可以推出 k k k 也整除N
,但由 ➀ 可知N
不被任何小于 K K K 的数整除,与假设矛盾;这样的 K K K 不可能作为最后的N
加入res
:循环退出的条件是i * i > N
,因此已经遍历完所有不超过 K \sqrt{K} K 的i
,即最后的N
不可能还有因子 k k k,与假设矛盾。
④ 最后证明 N N N 所有的质因子必然出现在res
中。假设 p p p 是 N N N 的一个质因子但不在res
中,显然 p p p 不可能是for
循环中的一个i
,那么它必然整除最后的N
,所以此时最后的N
必大于 1 1 1,由 ③ 可知最后的N
若大于 1 1 1 必为素数,因此此时最后的N
等于此 p p p,必会加入res
,与假设矛盾。
核心代码
void A_1008_solve () {
ans = n;
if (n == 1) ans = 0;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
ans = ans / i * (i - 1); // 先除后乘避免溢出
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1);
}
B. XTUOJ 1295 Flawless Prime
素性检测,模拟
- 模拟去掉最高位的过程,判断结果是否为素数即可
- 简单素性检测
bool is_prime (int n) { if (n < 2) return false; for (int i = 2; i * i <= n; ++i) if (n % i == 0) return false; return true; }
- 上面的素性检测基于这样一个事实:合数 N N N 的因子总是成对存在,设 x x x 为 N N N 的一个因子,则 x N \dfrac{x}{N} Nx 也是 N N N 的因子,且 x x x 和 x N \dfrac{x}{N} Nx 中必有一个不大于 N \sqrt{N} N(若两个都大于 N \sqrt{N} N,它们的积会大于 N N N,显然不可能)。因此对于数 N N N,素性检测只需检查它是否存在小于 N \sqrt{N} N 的因子即可。
核心代码
void B_1008_solve () {
ans = false;
int cnt = 0;
for (int nCopy = n; nCopy > 0; nCopy /= 10) {
if (nCopy % 10 == 0) return ; // 有前导 0 直接 return
digit[cnt++] = tmp % 10;
}
while (cnt--) {
if (!is_prime(n)) return ;
n -= digit[cnt] * pow_of_ten[cnt];
}
ans = true;
}
C. XTUOJ 1332 素数
素性检测,枚举
- 按题意枚举所有区间再判断是否为素数即可
核心代码
void C_1008_solve () {
ans = 0;
int len = strlen(n);
for (int left = 0; left < len; ++left) {
for (int right = left; right < len; ++right) {
int numFromInterval = 0;
for (int i = left; i <= right; ++i) {
if (i > left) numFromInterval *= 10;
numFromInterval = numFromInterval + n[k] - '0';
}
if (is_prime(numFromInterval)) ++ans;
}
}
}
D. XTUOJ 1524 不定方程的正整数解
分解质因数,数论函数
- 1 x − 1 y = 1 a \dfrac{1}{x} - \dfrac{1}{y} = \dfrac{1}{a} x1−y1=a1 可以写成 ( a + y ) ( a − x ) = a 2 (a + y)(a - x) = a^2 (a+y)(a−x)=a2,因此原问题可以转化为求 a 2 a^2 a2 有多少对不相等的因子
- 任意整数 N N N 的因子数个数可以由函数 τ ( n ) = ( e 1 + 1 ) ( e 2 + 1 ) … ( e k + 1 ) \tau(n) = (e_1 + 1)(e_2 + 1)\dots(e_k + 1) τ(n)=(e1+1)(e2+1)…(ek+1),其中 e 1 , e 2 , … , e k e_1, e_2, \dots ,e_k e1,e2,…,ek 来自于整数的唯一分解: N = p 1 e 1 p 2 e 2 … p k e k N = p_1^{e_1}p_2^{e_2} \dots p_k^{e_k} N=p1e1p2e2…pkek 中的指数。这个结论在整数唯一分解的基础上可以很容易由乘法计数原理得到
核心代码
void D_1008_solve () {
ans = 1;
for (int i = 2; i * i <= a; ++i) {
if (a % i == 0) {
int e = 0;
for (; a % i == 0; ++e) a /= i;
ans *= 2 * e + 1;
}
}
if (a != 1) ans *= 3; // a != 1 表示最后还余下一个质因子,指数为 1
ans = (ans - 1) / 2; // 去除 a * a = a ^ 2 的情况后除 2
}
E. XTUOJ 1172 因子和
数论函数
- 利用因子和函数 σ ( a ) = ∏ i = 1 k p i e i + 1 − 1 p i − 1 \sigma(a) = \prod_{i = 1}^{k}\dfrac{p_i^{e_i + 1} - 1}{p_i - 1} σ(a)=∏i=1kpi−1piei+1−1,其中 p 1 , p 2 , … , p k p_1, p_2, \dots, p_k p1,p2,…,pk 和 e 1 , e 2 , … , e k e_1, e_2, \dots ,e_k e1,e2,…,ek 来自于整数的唯一分解: N = p 1 e 1 p 2 e 2 … p k e k N = p_1^{e_1}p_2^{e_2} \dots p_k^{e_k} N=p1e1p2e2…pkek 中的质因数及其指数
- 函数中的 p e i + 1 p^{e_i + 1} pei+1 可以轻易地由唯一分解中得到,省去求幂
核心代码
void E_1008_solve () {
ans = 1;
for (int i = 2; i * i <= n; ++i) {
int tmp = 1;
while (n % i == 0) {
n /= i;
tmp *= i;
}
ans = ans * (tmp * i - 1) / (i - 1);
}
if (n > 1) ans *= (n + 1);
}
F. XTUOJ 1452 完全平方数 I
分解质因数
- a + ( a + 1 ) + ⋯ + a + n − 1 = ( a + a + n − 1 ) n 2 = n ( a + n − 1 2 ) a + (a + 1) + \dots + a + n - 1 = \dfrac{(a + a + n - 1) n}{2} = n(a + \dfrac{n - 1}{2}) a+(a+1)+⋯+a+n−1=2(a+a+n−1)n=n(a+2n−1)
- 易知上式是 n n n 的整数倍,若它为完全平方数,由 n n n 的唯一分解 n = ∏ i = 1 k p i e i n = \prod_{i = 1}^{k}p_i^{e_i} n=∏i=1kpiei,要使 n n n 的倍数为完全平方数,只需要把唯一分解中的所有奇指数配成偶数即可
- 若最后得到的结果小于 n − 1 2 \dfrac{n-1}{2} 2n−1,需要在此基础上再乘上一个完全平方数
核心代码
void F_1008_solve () {
ans = n == 1 ? 0 : 1;
int tmp = (n - 1) / 2;
for (int i = 2, e; i * i <= n; ++i) {
if (n % i == 0) {
for (e = 0; n % i == 0; ++e) n /= i;
if (e & 1) ans *= i;
}
}
if (n > 1) ans *= n;
for (int i = 2; ans < tmp; ++i) { // 结果小于 (n + 1) / 2,需要继续乘上一个完全平方数
if (ans * i * i >= tmp) {
ans = ans * i * i;
break;
}
}
ans -= tmp;
}
G. XTUOJ 1560 完全平方数 II
分解质因数
- 设 a x 2 + b x + c = k 2 ax^2 + bx + c = k^2 ax2+bx+c=k2,由于 x ∈ Z + x \in Z^+ x∈Z+,解方程得 x = − b + b 2 − 4 c + 4 k 2 2 x = \dfrac{-b + \sqrt{b^2 - 4c + 4k^2}}{2} x=2−b+b2−4c+4k2,易知当 b 2 − 4 c = 0 b^2 - 4c = 0 b2−4c=0 时,方程有无穷多个解,此外,设 b 2 − 4 c + 4 k 2 = t 2 b^2 -4c + 4k^2 = t^2 b2−4c+4k2=t2,因式分解得到 ( t − 2 k ) ( t + 2 k ) = b 2 − 4 c (t - 2k)(t + 2k) = b^2 - 4c (t−2k)(t+2k)=b2−4c
- 由上一步得到的最终表达式,可以求出 b 2 − 4 c b^2 - 4c b2−4c 的所有因子对,如果某对因子和能整除 2 2 2,可以认为该因子和是 2 t 2t 2t,由此可以求得 ∣ t ∣ \left \vert t \right \vert ∣t∣,再验证一下 − b + ∣ t ∣ -b + \left \vert t \right \vert −b+∣t∣ 是否大于 0 0 0 以及能否被 2 2 2 整除,若都可以就可以确定一个解
核心代码
int G_1008_solve () {
memset(ans, 0, sizeof(ans));
int n = b * b - 4 * c, cntOfAns = -1;
if (n == 0) return cntOfAns;
int nAbs = n > 0 ? n : -n;
cntOfAns = 0;
for (int i = 1, tmp; i * i <= nAbs; ++i) {
if (nAbs % i == 0) {
tmp = n > 0 ? nAbs / i + i : nAbs / i - i; // 求得 |2t|
if (tmp & 1 == 0) { // 验证求得的 |2t| 是否真地能被 2 整除
tmp /= 2;
tmp -= b;
}
else continue;
if (tmp > 0 && tmp & 1 == 0) ans[cntOfAns++] = tmp / 2;
}
}
return cntOfAns;
}
H. XTUOJ 1576 分段
分解质因数
- 对 A A A 的所有元素求和得到 S A S_A SA,最终的结果必然是 S A S_A SA 的因子
- 我们可以从小到大依次枚举 S A S_A SA 的所有因子,第一个符合条件的就是答案
核心代码
void H_1008_solve () {
// S[i] 表示 A[0] 到 A[i] 之和
ans = S[n - 1]; // 设置 ans 初始值为 S[A - 1](A 中所有元素的和)
int cnt = 0;
for (int i = 1; i * i <= S[n - 1]; ++i) if (S[n - 1] % i == 0) A[cnt++] = i;
if (cnt == 0) {
ans = 0;
return ;
}
for (int i = cnt - 1; i > 0; --i) A[cnt++] = S[n - 1] / A[i];
// 枚举和的 S[A - 1] 的所有因子
for (int i = 0; i < cnt; ++i) {
int tmp = 1;
for (int j = 0; j < n; ++j) {
if (S[j] % A[i] == 0) {
if (S[j] / A[i] == tmp) ++tmp;
else goto next;
}
}
if (A[i] < ans) ans = A[i];
next: ;
}
}