數學知識學習筆記
一、質數
略。
二、歐拉函數
\(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;
}