数学知识学习笔记
一、质数
略。
二、欧拉函数
\(1\) 到 \(n\) 中与 \(n\) 互质的数的个数称为欧拉函数,记做 \(\varphi(n)\)。
若在算术基本定理中,\(n = p_1^{c_1}p_2^{c_2}p_3^{c_3} \dots p_m^{c_m}\),则:
根据欧拉函数的计算柿,我们可以在分解质因数时「顺便」求出 \(\varphi(n)\)。代码:
int phi(int n) {
int ans = n;
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);
return ans;
}
欧拉函数的性质:
- 对于任意大于 \(1\) 的 \(n\),\(1\) 到 \(n\) 中与 \(n\) 互质的数的和为 \(n \times \varphi(n) \over 2\)。
- 若 \(a,b\) 互质,则 \(\varphi(ab) = \varphi(a)\varphi(b)\)。
- 设 \(p\) 为质数,若 \(p \mid n\) 且 \(p^2 \mid n\),则 \(\varphi(n) = \varphi({n \over p}) \times p\)。
- 设 \(p\) 为质数,若 \(p \mid n\) 但 \(p^2 \nmid n\),则 \(\varphi(n) = \varphi({n \over p}) \times (p-1)\)。
- \(\sum_{d \mid n} \varphi(d) = n\)。
如果当 \(a,b\) 互质时,有 \(f(ab) = f(a)f(b)\),那么称函数 \(f\) 为积性函数。显然,欧拉函数就是积性函数。
积性函数的性质:
若 \(f\) 是积性函数,且在算术基本定理中 \(n = \prod_{i=1}^m p_i^{c_i}\),则 \(f(n) = \prod_{i=1}^m f(p_i^{c_i})\)。
例题 2.2 [SDOI2008] 仪仗队
原题目链接:Link。
观察一下,我们可以发现,除了 \((0,1),(1,0),(1,1)\) 三个点外,一个点 \((x,y)\) 可以被看到,则 \(\gcd(x,y) = 1(1 \leq x,y \leq n, x \neq y)\)。
所以,我们只需要统计每个 \(2 \leq y \leq n\),有多少个 \(x\) 满足 \(\gcd(x,y)=1(1 \leq x \leq y)\),数量显然就为 \(\varphi(y)\)。因为这些点关于对角线对称,最后的答案就为 \(3 + \sum_{i=2}^{n-1} \varphi(i)\)。特殊地,若 \(n = 1\),则答案为 \(0\)。代码如下:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 4e4 + 5;
int phi[MAXN], n, ans;
int euler(int n) {
for (int i = 1; i <= n; i++) phi[i] = i;
for (int i = 2; i <= n; i++)
if (phi[i] == i)
for (int j = i; j <= n; j += i)
phi[j] = phi[j] / i * (i - 1);
} // 埃氏筛顺便得到 phi(i)
int main() {
cin >> n;
euler(n);
for (int i = 2; i < n; i++) ans += phi[i];
if (n == 1) puts("0");
else cout << ((ans << 1) + 3) << endl;
return 0;
}
当然,这样做的时间复杂度为 \(O(n \log \log n)\),而利用线性筛可以达到 \(O(n)\),核心思想就是:
设 \(p\) 为质数,若 \(p \mid n\) 且 \(p^2 \mid n\),则 \(\varphi(n) = \varphi({n \over p}) \times p\)。
设 \(p\) 为质数,若 \(p \mid n\) 但 \(p^2 \nmid n\),则 \(\varphi(n) = \varphi({n \over p}) \times (p-1)\)。
代码如下。
int v[MAXN], prime[MAXN], phi[MAXN];
void euler(int n) {
// memset(v, 0, sizeof(v))
// m = 0; // m 为质数数量
for (int i = 2; i <= n; i++) {
if (!v[i]) {prime[++m] = v[i] = i; phi[i] = i - 1;}
for (int j = 1; j <= m; j++) {
if (prime[j] > v[i] || i * prime[j] > n) break;
v[i * prime[j]] = prime[j];
phi[i * prime[j]] = phi[i] * (i % prime[j] ? prime[j] - 1 : prime[j]);
}
}
}
三、同余
若 \(a \bmod m = b \bmod m\),则称 \(a,b\) 模 \(m\) 同余,记作 \(a \equiv b \pmod{m}\)。
3.1 同余类与剩余系
前方一大堆恐怖芝士袭来!
对于任意 \([0, m - 1]\) 区间的 \(a\),集合 \(\{a+km\}(k \in \mathbb Z)\) 的所有数模 \(m\) 同余,余数都是 \(a\)。该集合称为一个模 \(m\) 的同余类,简记为 \(\overline{a}\)。
模 \(m\) 的同余类一共有 \(m\) 个,分别为 \(\overline{0},\overline{1},\overline{2},\dots,\overline{m-1}\),它们构成 \(m\) 的完全剩余系。
\(1\) 到 \(m\) 中与 \(m\) 互质的数代表的同余类一共有 \(\phi(m)\) 个,它们构成 \(m\) 的简化剩余系(比如,模 \(18\) 的简化剩余为 \(\{\overline{1},\overline{5},\overline{7},\overline{11},\overline{13}\}\)。
(形象化一点,\(\{1, 3, 5, 7, 9, \dots\}\) 的所有数模 \(2\) 同余,余数都是 \(1\)。)
简化剩余系关于模 \(m\) 乘法封闭。即若 \(a,b(1\leq a,b \leq m)\) 与 \(m\) 互质,则 \(a \times b \bmod m\) 也属于 \(m\) 的简化剩余系。
3.2 费马小定理
若 \(p\) 是质数,则对于任意整数 \(a\),有 \(a^p \equiv a \pmod{p}\)。
3.3 欧拉定理
若正整数 \(a,n\) 互质,则 \(a^{\varphi(n)} \equiv 1 \pmod{n}\)。
欧拉定理的推论:
若正整数 \(a,n\) 互质,则对于任意正整数 \(b\),有 \(a^b \equiv a^{b \bmod \varphi(n)} \pmod n\)。
例题 3.3.1 「高级算法」最幸运的数(POJ3696)
原题目链接:Link。
显然,\(x\) 个 \(8\) 连在一起形成的数就是 \(8 \times {(10^x - 1) \over 9}\)。
(推论:\(x\) 个 \(a\) 连在一起形成的数就是 \(a \times {(10^x - 1) \over 9}\)。)
那么,题目要求一个最小的 \(x\),满足 \(L \mid 8 \times {(10^x - 1) \over 9}\)。
我们设 \(d = \gcd(L, 8)\),则
\(L \mid 8 \times {(10^x - 1) \over 9} \to 9L \mid 8 \times (10^x - 1) \to \frac{9L}{d} \mid 10^x - 1 \to 10^x \equiv 1(\bmod \frac{9L}{d})\)
我们知道,若正整数 \(a,n\) 互质,则满足 \(a^x \equiv 1 (\bmod n)\) 的最小正整数 \(x_0\) 是 \(\varphi(n)\) 的因数(即 \(x_0 \mid \varphi(n)\))。所以,我们只要求出 \(\varphi({9L \over d})\) 的所有因数,再用快速幂检验即可。代码如下:
#include <bits/stdc++.h> // 各种 long long 开起来
using namespace std;
#define LL long long
#define ULL unsigned long long
#define PII pair<int, int>
#define M(x, y) make_pair(x, y)
const LL inf = 1e18;
LL L;
int tot;
LL phi(LL n) {
LL ans = n;
for (LL i = 2; i <= n / i; i++) {
if (n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1);
return ans;
} // 求一个数的 phi,记得开 long long
LL gcd(LL x, LL y) {return y ? gcd(y, x % y) : x;} // 求 gcd,可以 __gcd 代替
LL qm(LL a, LL b, LL k) {
LL ans = 0;
while (b) {
if (b & 1) ans = (ans + a) % k;
a = (a + a) % k;
b >>= 1;
}
return ans;
} // 龟速乘
LL q(LL a, LL b, LL k) {
LL ans = 1;
while (b) {
if (b & 1) ans = qm(ans, a, k);
a = qm(a, a, k);
b >>= 1;
}
return ans;
} // 快速幂
int main() {
while (scanf("%lld", &L) != EOF) {
tot++;
if (L == 0ll) return 0;
int d = gcd(8, L); L = L * 9 / d;
if (gcd(L, 10) > 1) {
puts("0");
continue;
}
LL k = phi(L), ans = inf;
for (LL i = 1; i <= k / i; i++)
if (k % i == 0) {
if (q(10, i, L) == 1) {
ans = i;
break; // 一定是最小的
}
if (q(10, k / i, L) == 1) ans = min(ans, k / i); // 这个不一定,所以不能直接 break
} // 枚举
printf("%lld\n", ans);
}
return 0;
}