Hướng dẫn giải của BEAUTY3
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Lời giải này đang bị ẩn cho đến khi bạn chọn mở ra.
Chúng tôi khuyên bạn nên tự thử giải bài trước. Việc mở lời giải có thể làm lộ mất ý tưởng chính trước khi bạn có cơ hội tự giải.
Bạn phải đăng nhập để mở lời giải này.
Đăng nhậpTác giả: , , ,
Hiểu bài toán
Bài toán yêu cầu tìm dãy các số tự nhiên 'đẹp'. Một số được gọi là đẹp nếu tổng lập phương của các chữ số của nó là một số nguyên tố. Với T truy vấn, mỗi truy vấn cho trước chỉ số N, yêu cầu tìm số thứ N trong dãy các số đẹp (sắp xếp theo thứ tự tự nhiên tăng dần).
Các cách tiếp cận
Cách Brute Force (Sàng số nguyên tố & Kiểm tra tuần tự)
#include <bits/stdc++.h>
using namespace std;
bool prime[8000];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
freopen("BEAUTY3.INP", "r", stdin);
freopen("BEAUTY3.OUT", "w", stdout);
// Sàng Eratosthenes để tìm số nguyên tố
// Giới hạn 8000 vì số lớn nhất của tổng lập phương 10 chữ số (10*9^3 = 7290)
for (int i = 2; i < 8000; i++) prime[i] = true;
for (int i = 2; i * i < 8000; i++)
if (prime[i])
for (int j = i * i; j < 8000; j += i)
prime[j] = false;
vector<int> beauty;
// Tạo mảng chứa các số đẹp
// Với N tối đa 1,000,000, ta cần khoảng 10,000,000 số đầu tiên để đủ số đẹp
beauty.reserve(8000000);
for (int x = 1; x <= 80000000; x++) {
int t = x, s = 0;
while (t) {
int d = t % 10;
s += d * d * d;
t /= 10;
}
if (prime[s]) beauty.push_back(x);
}
int T;
cin >> T;
while (T--) {
long long N;
cin >> N;
cout << beauty[N - 1] << "\n";
}
}
- Time Complexity: O(K) tiền xử lý (với K ~ 8*10^7), O(1) truy vấn
- Space Complexity: O(K) (khoảng 8*10^7 phần tử)
Phương pháp này tiền xử lý toàn bộ kết quả trước khi trả lời truy vấn.
- Sàng số nguyên tố jusqu'à 8000.
- Duyệt qua các số tự nhiên từ 1 đến khoảng 80 triệu, tính tổng lập phương các chữ số (TLPFCS).
- Nếu TLPFCS là số nguyên tố, lưu số đó vào mảng.
- Với mỗi truy vấn N, chỉ cần truy cập mảng tại chỉ số N-1. Đây là cách làm trực quan nhất nhưng tốn bộ nhớ và thời gian chạy vòng lặp lớn.
Cách Tối ưu hóa (Sàng & Duyệt)
#include <bits/stdc++.h>
using namespace std;
const int MAX_SIEVE = 8000;
vector<bool> is_prime(MAX_SIEVE, true);
vector<int> beautiful;
void sieve() {
is_prime[0] = is_prime[1] = false;
for (int i = 2; i * i < MAX_SIEVE; ++i) {
if (is_prime[i]) {
for (int j = i * i; j < MAX_SIEVE; j += i)
is_prime[j] = false;
}
}
}
int sum_cubes(int n) {
int s = 0;
while (n > 0) {
int d = n % 10;
s += d * d * d;
n /= 10;
}
return s;
}
int main() {
ios_base::sync_with_stdio(false); cin.tie(0);
freopen("BEAUTY3.INP", "r", stdin);
freopen("BEAUTY3.OUT", "w", stdout);
sieve();
// Tính toán số đẹp
// Dựa trên quan sát, mật độ số đẹp khá cao.
// Để có 10^6 số đẹp, cần duyệt khoảng 10^7 số.
// Ta dùng mảng động để tiết kiệm vùng nhớ stack.
int limit = 15000000;
beautiful.reserve(limit);
for (int x = 1; beautiful.size() < 1000000; x++) {
if (is_prime[sum_cubes(x)]) {
beautiful.push_back(x);
}
}
int T; cin >> T;
while (T--) {
int n; cin >> n;
cout << beautiful[n-1] << "\n";
}
return 0;
}
- Time Complexity: O(K) tiền xử lý (với K ~ 1.5*10^7), O(1) truy vấn
- Space Complexity: O(N) (Lưu trữ khoảng 1 triệu số đẹp)
Đây là phiên bản tối ưu hơn của Approach 1.
- Thay vì dùng mảng c cứng, dùng
vectorcho linh hoạt. - Quan trọng nhất: Vòng lặp
fordừng lại ngay khi đủ số lượng đẹp cần thiết (ví dụ 1 triệu số). Điều này tránh việc duyệt qua các số lớn không cần thiết. - Tách hàm tính tổng lập phương để code rõ ràng hơn.
- Tối ưu I/O.
Cách Tối ưu hóa Logic (Thay đổi giới hạn)
#include <iostream>
#include <vector>
#include <cmath>
#include <fstream>
#include <algorithm>
using namespace std;
const int SIEVE_LIMIT = 8000;
const int MAX_BEAUTIFUL = 1000000;
vector<bool> is_p(SIEVE_LIMIT + 1, true);
vector<int> beautiful_numbers;
void sieve() {
is_p[0] = is_p[1] = false;
for (int p = 2; p * p <= SIEVE_LIMIT; p++) {
if (is_p[p]) {
for (int i = p * p; i <= SIEVE_LIMIT; i += p)
is_p[i] = false;
}
}
}
int sum_of_cubes(long long n) {
int sum = 0;
string s = to_string(n);
for (char c : s) {
int digit = c - '0';
sum += digit * digit * digit;
}
return sum;
}
void generate_beautiful_numbers() {
long long current_num = 1;
while (beautiful_numbers.size() < MAX_BEAUTIFUL) {
int sum_cub = sum_of_cubes(current_num);
if (sum_cub < SIEVE_LIMIT && is_p[sum_cub]) {
beautiful_numbers.push_back(current_num);
}
current_num++;
}
}
int main() {
sieve();
generate_beautiful_numbers();
ifstream in("BEAUTY3.INP");
ofstream out("BEAUTY3.OUT");
int T;
in >> T;
while (T--) {
long long N;
in >> N;
out << beautiful_numbers[N - 1] << "\n";
}
return 0;
}
- Time Complexity: O(K) tiền xử lý, O(1) truy vấn
- Space Complexity: O(N) (Lưu trữ kết quả)
Phương pháp này nhấn mạnh vào việc tối ưu hóa vòng lặp bằng cách:
- Sử dụng
to_stringđể tính tổng lập phương (có thể chậm hơn toán học nhưng code ngắn, dễ hiểu). - Kiểm tra điều kiện
sum_cub < SIEVE_LIMITtrước khi truy cập mảng sàng để tránh lỗi truy cập ngoài vùng nhớ. - Đây là cách tiếp cận chuẩn trong các kỳ thi lập trình: Tiền xử lý toàn bộ dữ liệu vào mảng, sau đó trả lời truy vấn trong thời gian O(1).
Phân tích độ phức tạp
| Cách tiếp cận | Time | Space | Tên |
|---|---|---|---|
| 1 | O(K) tiền xử lý (với K ~ 8*10^7), O(1) truy vấn | O(K) (khoảng 8*10^7 phần tử) | Brute Force (Sàng số nguyên tố & Kiểm tra tuần tự) |
| 2 | O(K) tiền xử lý (với K ~ 1.5*10^7), O(1) truy vấn | O(N) (Lưu trữ khoảng 1 triệu số đẹp) | Tối ưu hóa (Sàng & Duyệt) |
| 3 | O(K) tiền xử lý, O(1) truy vấn | O(N) (Lưu trữ kết quả) | Tối ưu hóa Logic (Thay đổi giới hạn) |
Bài học kinh nghiệm
- Tổng lập phương của các chữ số của một số lớn (tới 10^8) không vượt quá 7290 (10 * 9^3). Do đó, ta chỉ cần sàng số nguyên tố đến 8000 là đủ.
- Mật độ số đẹp khá cao, để tìm được 1,000,000 số đẹp, ta chỉ cần duyệt qua khoảng 10,000,000 - 15,000,000 số tự nhiên đầu tiên.
- Bài toán này phù hợp với phương pháp tiền xử lý (Pre-computation): Duyệt một lần, trả lời nhiều truy vấn.
Lỗi thường gặp
- Không xác định được giới hạn sàng số nguyên tố (Sieve Limit) đúng, dẫn đến lỗi khi tính tổng lập phương.
- Duyệt quá nhiều số không cần thiết làm chậm chương trình (ví dụ duyệt tới 80 triệu số thay vì 15 triệu).
- Sử dụng
cin/coutkhông tắt đồng bộ hóa I/O dẫn đến trễ thời gian chạy.
Bình luận