Bài 11: Lũy Thừa Nhị Phân & Sàng Nguyên Tố¶
Tác giả: FPTOJ Team
Nội dung tham khảo từ: VNOI Wiki - Lũy thừa nhị phân, Sàng nguyên tố
Bản chất vấn đề¶
Lũy thừa nhị phân (Binary Exponentiation)¶
Cho số nguyên \(a\) và số mũ \(b\) (có thể lên đến \(10^{18}\)). Cần tính \(a^b\) hoặc \(a^b \bmod p\).
Cách tiếp cận naïve: Nhân \(a\) với chính nó \(b\) lần → \(O(b)\) phép nhân. Với \(b = 10^{18}\), thuật toán chạy không kịp.
Cách tiếp cận tối ưu: Tận dụng biểu diễn nhị phân của \(b\) để tính trong \(O(\log b)\) phép nhân. Đây chính là Binary Exponentiation.
Sàng nguyên tố Eratosthenes¶
Cho số nguyên \(N\) (có thể đến \(10^7\) hoặc lớn hơn). Cần liệt kê tất cả số nguyên tố từ \(2\) đến \(N\), hoặc tiền xử lý để kiểm tra nguyên tố / phân tích thừa số cho nhiều truy vấn.
Cách tiếp cận naïve: Kiểm tra từng số \(i\) có phải nguyên tố không bằng cách thử chia → \(O(N\sqrt{N})\).
Cách tiếp cận tối ưu: Dùng Sàng Eratosthenes — đánh dấu bội số của mỗi nguyên tố → \(O(N \log \log N)\).
Tư duy cốt lõi¶
1. Binary Exponentiation¶
Ý tưởng cốt lõi¶
Thay vì tính \(a^b\) bằng \(b\) phép nhân, ta khai thác quy tắc:
Mỗi bước chia đôi số mũ → chỉ cần \(O(\log b)\) bước.

Kết nối với biểu diễn nhị phân¶
Số mũ \(b\) có thể viết dưới dạng nhị phân. Mỗi bit bằng \(1\) tương ứng với một lũy thừa của \(a\) cần nhân vào kết quả:
| Bit vị trí | Giá trị | Lũy thừa tương ứng |
|---|---|---|
| 0 | \(2^0 = 1\) | \(a^1\) |
| 1 | \(2^1 = 2\) | \(a^2\) |
| 2 | \(2^2 = 4\) | \(a^4\) |
| 3 | \(2^3 = 8\) | \(a^8\) |
| \(k\) | \(2^k\) | \(a^{2^k}\) |
Ví dụ: \(b = 13 = 1101_2 = 2^3 + 2^2 + 2^0\) → \(a^{13} = a^8 \cdot a^4 \cdot a^1\).
Minh họa từng bước: Tính \(3^{10}\)¶
\(10 = 1010_2\), tức \(2^3 + 2^1 = 8 + 2\).
| Bước | \(a\) (lũy thừa tích lũy) | \(b\) (số mũ còn lại) | Bit hiện tại | \(result\) | Hành động |
|---|---|---|---|---|---|
| 1 | \(3^1 = 3\) | \(10\) | \(0\) | \(1\) | \(b\) chẵn → chỉ nhân đôi \(a\) |
| 2 | \(3^2 = 9\) | \(5\) | \(1\) | \(1 \times 9 = 9\) | \(b\) lẻ → nhân \(result\), rồi nhân đôi \(a\) |
| 3 | \(3^4 = 81\) | \(2\) | \(0\) | \(9\) | \(b\) chẵn → chỉ nhân đôi \(a\) |
| 4 | \(3^8 = 6561\) | \(1\) | \(1\) | \(9 \times 6561 = 59049\) | \(b\) lẻ → nhân \(result\). \(b = 0\), dừng! |
Kết quả: \(3^{10} = 59049\).
Quy trình thuật toán¶
Các trường hợp biên¶
| Trường hợp | Kết quả | Ghi chú |
|---|---|---|
| \(a^0\) | \(1\) | Bất kỳ số nào mũ 0 đều bằng 1 |
| \(a^1\) | \(a\) | Chỉ 1 bước |
| \(0^b\) (\(b > 0\)) | \(0\) | Luôn bằng 0 |
| \(1^b\) | \(1\) | Luôn bằng 1 |
| \(a^b \bmod 1\) | \(0\) | Modulo 1 luôn bằng 0 |
Code¶
Phân tích tính đúng đắn¶
Bất biến (Invariant): Sau mỗi vòng lặp, luôn đúng \(result \times a^b = a^{b_{\text{ban đầu}}}\), trong đó \(b\) là giá trị hiện tại của số mũ.
- Khởi tạo: \(result = 1\), \(b = b_0\) → \(1 \times a^{b_0} = a^{b_0}\). Đúng.
- Mỗi bước:
- Nếu \(b\) lẻ: \(result' = result \times a\), \(a' = a^2\), \(b' = \lfloor b/2 \rfloor\). Khi đó \(result' \times (a')^{b'} = result \times a \times (a^2)^{\lfloor b/2 \rfloor} = result \times a^{1 + 2\lfloor b/2 \rfloor} = result \times a^b\). Đúng.
- Nếu \(b\) chẵn: \(result' = result\), \(a' = a^2\), \(b' = b/2\). Khi đó \(result' \times (a')^{b'} = result \times (a^2)^{b/2} = result \times a^b\). Đúng.
- Kết thúc: \(b = 0\) → \(result \times a^0 = result = a^{b_0}\). Đúng.
Đánh giá độ phức tạp¶
| Giá trị | |
|---|---|
| Thời gian | \(O(\log b)\) — mỗi bước chia đôi \(b\) |
| Bộ nhớ | \(O(1)\) — chỉ dùng vài biến |
| Số phép nhân | \(\lfloor \log_2 b \rfloor\) phép nhân đôi \(a\) + tối đa \(\lfloor \log_2 b \rfloor\) phép nhân vào \(result\) |
Bảng tra cứu nhanh:
| \(b\) | Số bước |
|---|---|
| \(10\) | \(4\) |
| \(10^6\) | \(20\) |
| \(10^9\) | \(30\) |
| \(10^{18}\) | \(60\) |
2. Ứng dụng: Tính \(\binom{n}{k} \bmod p\)¶
Để tính \(\binom{n}{k} = \frac{n!}{k!(n-k)!} \pmod{p}\) với \(p\) là số nguyên tố (thường \(10^9 + 7\)), ta cần nghịch đảo modulo. Dùng định lý Fermat nhỏ: \(a^{-1} \equiv a^{p-2} \pmod{p}\).
Ý tưởng¶
- Precompute \(fact[i] = i! \bmod p\) và \(inv\_fact[i] = (i!)^{-1} \bmod p\)
- \(inv\_fact[i]\) có thể tính từ \(inv\_fact[i+1]\): \((i!)^{-1} = ((i+1)!)^{-1} \times (i+1)\)
- \(\binom{n}{k} = fact[n] \times inv\_fact[k] \times inv\_fact[n-k] \bmod p\)
Code¶
3. Ứng dụng: Ma trận lũy thừa (Matrix Exponentiation)¶
Nhiều bài toán dãy số có dạng truy hồi \(f(n) = a \cdot f(n-1) + b \cdot f(n-2)\), cần tính \(f(n)\) với \(n\) rất lớn (\(10^{18}\)). Dùng ma trận lũy thừa:
Tính ma trận mũ trong \(O(k^3 \log n)\) với \(k\) là kích thước ma trận.
Code: Fibonacci trong \(O(\log n)\)¶
4. Sàng Eratosthenes¶
Bản chất vấn đề¶
Cho \(N\), cần đánh dấu tất cả số nguyên tố từ \(2\) đến \(N\).
Tư duy cốt lõi¶
Bắt đầu từ số nguyên tố nhỏ nhất là \(2\). Đánh dấu tất cả bội số của \(2\) (trừ chính \(2\)). Chuyển sang số lớn hơn chưa bị đánh dấu — đó là \(3\) — đánh dấu bội số của \(3\). Tiếp tục cho đến \(\sqrt{N}\).
Tại sao chỉ cần duyệt đến \(\sqrt{N}\)? Nếu \(N\) có ước nguyên tố \(p > \sqrt{N}\), thì \(N/p < \sqrt{N}\), nghĩa là \(N\) đã bị đánh dấu bởi ước nhỏ hơn.
Tại sao bắt đầu đánh dấu từ \(i^2\)? Với số nguyên tố \(i\), các bội \(2i, 3i, \ldots, (i-1) \cdot i\) đã bị đánh dấu bởi các số nguyên tố nhỏ hơn \(i\). Bội đầu tiên chưa bị đánh dấu là \(i \times i = i^2\).
Minh họa sàng với \(N = 30\)¶
Kết quả sau khi sàng: các số không bị đánh dấu là số nguyên tố: \(\{2, 3, 5, 7, 11, 13, 17, 19, 23, 29\}\).
Code¶
Phân tích tính đúng đắn¶
Bất biến: Sau khi xử lý xong số nguyên tố \(p\), tất cả bội của \(p\) trong \([2, N]\) đã bị đánh dấu là hợp số.
- Khởi tạo: Tất cả số từ \(2\) đến \(N\) được giả sử là nguyên tố (
true). - Mỗi bước \(i\): Nếu \(i\) chưa bị đánh dấu (tức \(i\) là nguyên tố), tất cả bội \(j = i^2, i^2 + i, i^2 + 2i, \ldots\) được đánh dấu là hợp số. Các bội \(2i, 3i, \ldots, (i-1)i\) đã bị đánh dấu trước đó bởi các nguyên tố nhỏ hơn.
- Dừng tại \(\sqrt{N}\): Mọi hợp số \(n \le N\) đều có ước nguyên tố \(\le \sqrt{N}\), nên đã bị đánh dấu.
- Kết luận: Mọi số còn lại chưa bị đánh dấu đều là nguyên tố.
Đánh giá độ phức tạp¶
| Giá trị | |
|---|---|
| Thời gian | \(O(N \log \log N)\) |
| Bộ nhớ | \(O(N)\) |
Chi tiết: Với mỗi nguyên tố \(p\), ta đánh dấu \(\approx N/p\) bội số. Tổng:
Bảng giới hạn thực tế:
| \(N\) | Bộ nhớ (bool) |
Thời gian | Ghi chú |
|---|---|---|---|
| \(10^6\) | \(\approx 1\) MB | \(< 0.01\)s | Rất OK |
| \(10^7\) | \(\approx 10\) MB | \(\approx 0.05\)s | OK |
| \(10^8\) | \(\approx 100\) MB | \(\approx 0.5\)s | Có thể thiếu bộ nhớ |
| \(10^9\) | \(\approx 1\) GB | \(\approx 5\)s | Phải dùng sàng phân đoạn |
5. Sàng SPF (Smallest Prime Factor)¶
Bản chất vấn đề¶
Precompute thừa số nguyên tố nhỏ nhất (Smallest Prime Factor) cho mọi số từ \(1\) đến \(N\). Sau đó, phân tích thừa số nguyên tố của bất kỳ số nào trong \(O(\log N)\).
Tư duy cốt lõi¶
Khởi đầu: \(spf[i] = i\) cho mọi \(i\). Duyệt \(i\) từ \(2\) đến \(\sqrt{N}\): nếu \(spf[i] = i\) (tức \(i\) là nguyên tố), đánh dấu \(spf[j] = i\) cho mọi bội \(j\) của \(i\) mà chưa có SPF nhỏ hơn.
Sau khi sàng, để phân tích thừa số của \(n\): lặp lại lấy \(spf[n]\), chia \(n\) cho \(spf[n]\) cho đến khi \(n = 1\).
Code¶
Phân tích tính đúng đắn¶
Bất biến: Sau khi sàng xong, \(spf[n]\) luôn là ước nguyên tố nhỏ nhất của \(n\).
- Với mỗi nguyên tố \(i\), ta duyệt bội \(j = i^2, i^2 + i, \ldots\). Nếu \(spf[j] = j\) (chưa được gán), gán \(spf[j] = i\).
- Vì ta duyệt \(i\) tăng dần, giá trị gán đầu tiên cho \(spf[j]\) chính là nguyên tố nhỏ nhất chia hết \(j\).
- Khi phân tích: mỗi bước lấy \(spf[n]\) rồi chia \(n\) cho \(spf[n]\), đảm bảo lấy đúng thừa số nguyên tố nhỏ nhất còn lại.
Đánh giá độ phức tạp¶
| Giá trị | |
|---|---|
| Precompute | \(O(N \log \log N)\) — tương đương sàng Eratosthenes |
| Mỗi truy vấn phân tích | \(O(\log N)\) — mỗi bước chia ít nhất một lần |
| Bộ nhớ | \(O(N)\) |
6. Sàng đếm ước¶
Dùng kỹ thuật tương tự sàng Eratosthenes để đếm số ước của mọi số từ \(1\) đến \(N\).
Code¶
Đánh giá độ phức tạp¶
| Giá trị | |
|---|---|
| Thời gian | \(O(N \log N)\) — tổng harmonic series: \(\sum_{i=1}^{N} \frac{N}{i} = N \cdot H_N \approx N \ln N\) |
| Bộ nhớ | \(O(N)\) |
7. Sàng phân đoạn (Segmented Sieve)¶
Khi \(N\) rất lớn (\(10^9\) trở lên), sàng Eratosthenes thường tốn quá nhiều bộ nhớ. Sàng phân đoạn chia khoảng \([L, R]\) thành các đoạn nhỏ, sàng từng đoạn.
Tư duy cốt lõi¶
- Sàng nhỏ: tìm tất cả nguyên tố \(\le \sqrt{R}\) bằng Eratosthenes thường
- Với mỗi nguyên tố \(p\) tìm được, đánh dấu bội của \(p\) trong khoảng \([L, R]\)
- Các số chưa bị đánh dấu trong \([L, R]\) là nguyên tố
Code¶
Đánh giá độ phức tạp¶
| Giá trị | |
|---|---|
| Thời gian | \(O((R - L + 1) \log \log R + \sqrt{R} \log \log \sqrt{R})\) |
| Bộ nhớ | \(O(\sqrt{R} + (R - L + 1))\) |
Bẫy thường gặp & Mẹo thi đấu¶
Bẫy 1: Tràn số khi tính lũy thừa¶
Khi \(\text{MOD} \approx 10^9\), phép nhân \(a \times a\) có thể vượt quá long long (tối đa \(\approx 9.2 \times 10^{18}\)).
Bẫy 2: Quên modulo ở mỗi bước¶
Chỉ modulo ở kết quả cuối sẽ gây tràn số ngay. Phải modulo sau mỗi phép nhân:
Bẫy 3: \(i \times i\) tràn int¶
Bẫy 4: SPF quên khởi đầu đúng¶
Bảng tổng hợp: Khi nào dùng gì?¶
| Bài toán | Dùng gì | Độ phức tạp |
|---|---|---|
| Tính \(a^b \bmod \text{MOD}\) | powerMod(a, b, MOD) |
\(O(\log b)\) |
| Tính Fibonacci \(F(n)\), \(n = 10^{18}\) | Matrix Exponentiation | \(O(8 \log n)\) |
| Kiểm tra nguyên tố \(N \le 10^7\) | Sàng Eratosthenes precompute | \(O(1)\) / truy vấn |
| Kiểm tra nguyên tố \(N \le 10^{12}\) | Miller-Rabin (nâng cao) | \(O(k \log^2 N)\) |
| Phân tích thừa số nhiều lần | Sàng SPF precompute | \(O(\log N)\) / truy vấn |
| Đếm ước của \(N\) | Phân tích thừa số → nhân \((a_i + 1)\) | \(O(\log N)\) với SPF |
| Tính \(\binom{n}{k} \bmod p\) | Precompute fact + inv_fact | \(O(1)\) / truy vấn |
Bài tập luyện tập¶
Cơ bản — Lũy thừa nhị phân¶
| Bài | Nền tảng | Độ khó | Chủ đề | Ghi chú |
|---|---|---|---|---|
| CSES - Exponentiation | CSES | ⭐⭐ | \(a^b \bmod p\) | Bài khởi đầu, luyện code |
| CSES - Exponentiation II | CSES | ⭐⭐⭐ | \(a^{b^c} \bmod p\) | Fermat nhỏ: \(a^{b^c \bmod (p-1)}\) |
| CSES - Counting Necklaces | CSES | ⭐⭐⭐ | Lũy thừa mod | Ứng dụng power mod |
| VNOJ - VPOWER | VNOJ | ⭐⭐ | Power mod | Lũy thừa nhị phân cơ bản |
Cơ bản — Sàng nguyên tố¶
| Bài | Nền tảng | Độ khó | Chủ đề | Ghi chú |
|---|---|---|---|---|
| CSES - Primes | CSES | ⭐⭐ | Liệt kê nguyên tố | Sàng Eratosthenes |
| CSES - Counting Divisors | CSES | ⭐⭐ | Đếm ước | Sàng đếm ước hoặc SPF |
| CSES - Binomial Coefficients | CSES | ⭐⭐ | \(\binom{n}{k} \bmod p\) | Precompute fact + inv_fact |
| SPOJ - Prime Generator | SPOJ | ⭐⭐ | Sàng phân đoạn | In nguyên tố trong \([m, n]\) |
| VNOJ - NTBONUS | VNOJ | ⭐⭐ | Sàng + đếm | Ứng dụng sàng |
Trung bình — Kết hợp¶
| Bài | Nền tảng | Độ khó | Chủ đề | Ghi chú |
|---|---|---|---|---|
| Codeforces - Almost Prime | CF | ⭐⭐ | Đếm thừa số | Sàng + đếm số ước nguyên tố |
| Codeforces - T-primes | CF | ⭐⭐ | Kiểm tra T-prime | T-prime = bình phương nguyên tố |
| Codeforces - Noldbach Problem | CF | ⭐⭐ | Nguyên tố liên tiếp | Sàng + kiểm tra |
| CSES - Common Divisors | CSES | ⭐⭐⭐ | GCD lớn nhất | Sàng ước hoặc sieve-style |
| VNOJ - VOMARBLE | VNOJ | ⭐⭐⭐ | Combinatorics | \(\binom{n}{k} \bmod p\) |
Nâng cao¶
| Bài | Nền tảng | Độ khó | Chủ đề | Ghi chú |
|---|---|---|---|---|
| Codeforces - Christmas Trees | CF | ⭐⭐⭐⭐ | Sắp xếp + chia ước | Sàng thừa số + greedy |
| CSES - Prime Multiples | CSES | ⭐⭐⭐⭐ | Nguyên lý bao hàm | Sàng nguyên tố + bitmask |
| SPOJ - DIVSUM | SPOJ | ⭐⭐ | Tổng ước | Sàng tổng ước |
| Codeforces - Smash the Rocks | CF | ⭐⭐⭐⭐ | Matrix exponentiation | Ma trận lũy thừa |
Bài viết liên quan¶
Tài liệu tham khảo¶
- VNOI Wiki - Lũy thừa nhị phân
- VNOI Wiki - Sàng nguyên tố
- CP-Algorithms - Binary Exponentiation
- CP-Algorithms - Sieve of Eratosthenes
- HackerEarth - Number Theory
Bài tiếp theo: Quy hoạch động →