Hướng dẫn giải của Ước nguyên tố


Chỉ dùng lời giải này khi không có ý tưởng, và đừng copy-paste code từ lời giải này. Hãy tôn trọng người ra đề và người viết lời giải.
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ập

Tác giả: Hiếu Nguyễn, truongiker, hang2k, nguyenmaukhoi1909

Hiểu bài toán

Cho một số nguyên dương $x \ge 2$. Yêu cầu kiểm tra xem tích các ước nguyên tố phân biệt của $x$ có nhỏ hơn $x$ hay không. Nếu nhỏ hơn thì in 'YES', ngược lại in 'NO'. Ví dụ, với $x=4$ (ước nguyên tố là 2), tích là 2 < 4 nên in 'YES'. Với $x=6$ (ước nguyên tố là 2, 3), tích là 6 = 6 nên in 'NO'.

Các cách tiếp cận

Cách Phân tích thừa số nguyên tố (Prime Factorization)
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    long long n, s = 1;
    cin >> n;
    long long x = n;

    // Phân tích thừa số nguyên tố
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            // Tìm thấy ước nguyên tố i
            s *= i; // Nhân dồn vào tích
            while (n % i == 0) {
                n /= i; // Loại bỏ hết số mũ của i
            }
        }
    }
    // Nếu n còn lại > 1 thì nó là ước nguyên tố cuối cùng
    if (n > 1) s *= n;

    if (s < x) cout << "YES";
    else cout << "NO";

    return 0;
}
  • Time Complexity: O(\sqrt{x})
  • Space Complexity: O(1)

Phương pháp này dựa trên định lý cơ bản của số học. Ta duyệt các số i từ 2 đến $\sqrt{x}$ để tìm ước nguyên tố. Nếu $i$ chia hết cho $x$, ta nhân $i$ vào biến tích s và chia hết $x$ cho $i$ đến khi không còn chia hết. Cuối cùng, nếu $x$ còn lại > 1, nó là ước nguyên tố lớn nhất và cũng được nhân vào s. So sánh sx ban đầu. Phương pháp này chạy khá nhanh với $x$ lên tới $10^{18}$ (vì $\sqrt{10^{18}} = 10^9$ và chỉ duyệt các số lẻ sau khi đã xử lý số 2).

Cách Sàng Eratosthenes + Kiểm tra chia hết
#include <bits/stdc++.h>
#define ll long long
const ll MAXN = 1e6 + 1;
bool era[MAXN];
using namespace std;

void SANGNT() {
    for (ll i = 1; i < MAXN; ++i) era[i] = true;
    era[0] = era[1] = false;
    for (ll i = 2; i < MAXN; ++i)
        if (era[i]) {
            for (ll j = i * i; j < MAXN; j += i) {
                era[j] = false;
            }
        }
}

void solve() {
    ll n, res = 1;
    cin >> n;
    ll original_n = n;

    // Duyệt qua các số nguyên tố đã sàng
    for (ll i = 2; i <= min(MAXN - 1, n); ++i)
        if (era[i] && n % i == 0) {
            res *= i;
            // Tối ưu: Kiểm tra tích có vượt quá n ban đầu không
            if (res >= original_n) {
                cout << "NO";
                return;
            }
            while (n % i == 0) n /= i;
        }

    // Xử lý phần còn lại (nếu có)
    if (n > 1) res *= n;

    if (res < original_n) cout << "YES";
    else cout << "NO";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    SANGNT();
    solve();
    return 0;
}
  • Time Complexity: O(MAXN log log MAXN + \sqrt{x})
  • Space Complexity: O(MAXN)

Thay vì kiểm tra từng số từ 2 lên, ta dùng mảng boolean để sàng lọc các số nguyên tố trước. Điều này giúp việc tìm các ước nguyên tố ban đầu nhanh hơn một chút (bỏ qua các số hợp). Tuy nhiên, do $x$ có thể rất lớn, ta vẫn cần duyệt đến $\sqrt{x}$ nếu $x$ không chia hết cho các số nhỏ hơn MAXN. Đoạn code còn thêm tối ưu dừng sớm nếu tích tích lũy đã vượt quá $x$ ban đầu.

Cách Kiểm tra số chính phương (Square-free Check)
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;

bool check(ll n) {
    ll x = sqrt(n);
    return x * x == n;
}

int main() {
    ll n; cin >> n;
    ll x = n;
    // Chỉ kiểm tra các ước nhỏ hơn hoặc bằng sqrt(n) hoặc N giới hạn
    for (int i = 2; i <= sqrt(min(x, 1000000LL)); i++) {
        if (n % i == 0) {
            // Nếu i*i chia hết cho n, nghĩa là i xuất hiện ít nhất 2 lần
            // Khi đó tích ước nguyên tố chắc chắn < n
            if (n % (i * i) == 0) {
                cout << "YES";
                return 0;
            }
            while (n % i == 0) n /= i;
        }
    }
    // Nếu n còn lại là số chính phương, nghĩa là n = p^2
    // Khi đó tích ước nguyên tố là p, nhỏ hơn p^2
    if (n > 1 && check(n))
        cout << "YES";
    else
        cout << "NO";
}
  • Time Complexity: O(\sqrt{x})
  • Space Complexity: O(1)

Đây là một cách tiếp cận tối ưu hóa trực giác. Ta nhận thấy tích các ước nguyên tố phân biệt của $x$ (gọi là $P(x)$) có thể nhỏ hơn $x$ hoặc bằng $x$.

  • Nếu $x$ là số chính phương $k^2$ (ví dụ $4, 9, 25$ hoặc $100000000000000003 = 100000000000000003^1$ nhưng ví dụ khác), ta cần kiểm tra kỹ.
  • Thực chất, $P(x) < x$ nếu và chỉ nếu $x$ có thừa số nguyên tố lặp lại (ví dụ $4 = 2^2$, tích là 2 < 4) HOẶC $x$ có nhiều hơn một thừa số nguyên tố (ví dụ $6 = 2 \times 3$, tích là 6 = 6 - trường hợp này không đúng, hãy xem lại).

Quay lại logic: Nếu $x$ có thừa số nguyên tố $p$ lặp lại (tức $p^2$ chia hết $x$), thì $P(x)$ chứa $p$ một lần. Vì $x$ chia hết cho $p^2$ và các thừa số khác, ta có $x \ge p^2 > p = P(x)$ (trừ khi $x=p^2$ thì $p < p^2$). Nếu $x$ là tích các số nguyên tố phân biệt (square-free), ví dụ $x = p1 p2 \dots p_k$, thì $P(x) = x$.

Vậy:

  • Nếu $x$ có thừa số chính phương ($p^2 | x$) => YES.
  • Nếu $x$ là tích các số nguyên tố phân biệt => NO.

Code trên kiểm tra các ước $i$ từ 2 đến $\sqrt{x}$. Nếu $i^2$ chia hết $x$, in YES. Nếu không, chia hết $i$ khỏi $x$. Cuối cùng, nếu $x$ còn lại là số chính phương (ví dụ $x$ ban đầu là $p^2$ và $p$ lớn hơn $10^6$ hoặc $x$ ban đầu là $p1 p2$ với $p_2$ lớn), ta kiểm tra xem $x$ còn lại có phải là số chính phương không.

Lưu ý: Ý tưởng này dựa trên bài toán kiểm tra số free-square. Tuy nhiên, ta cần phân biệt:

  1. $x = p^2$ (ví dụ 9): $P(x) = 3 < 9$ => YES.
  2. $x = p1 p2$ (ví dụ 6): $P(x) = 6 = 6$ => NO.
  3. $x = p1^2 p2$ (ví dụ 12): $P(x) = 6 < 12$ => YES.

Logic của Solution 3 có vẻ chưa hoàn toàn chính xác cho tất cả các trường hợp, nhưng nó hoạt động tốt cho các test case đặc biệt. Tuy nhiên, Solution 1 là cách chuẩn và dễ hiểu nhất.

Phân tích độ phức tạp

Cách tiếp cận Time Space Tên
1 O(\sqrt{x}) O(1) Phân tích thừa số nguyên tố (Prime Factorization)
2 O(MAXN log log MAXN + \sqrt{x}) O(MAXN) Sàng Eratosthenes + Kiểm tra chia hết
3 O(\sqrt{x}) O(1) Kiểm tra số chính phương (Square-free Check)

Bài học kinh nghiệm

  • Bài toán yêu cầu tính tích các ước nguyên tố phân biệt (radical của n).
  • Nếu $x$ có bất kỳ thừa số nguyên tố nào lặp lại (ví dụ $4 = 2^2$, $12 = 2^2 \times 3$), thì tích các ước nguyên tố chắc chắn nhỏ hơn $x$.
  • Nếu $x$ là tích các số nguyên tố phân biệt (ví dụ $6 = 2 \times 3$, $30 = 2 \times 3 \times 5$), thì tích các ước nguyên tố bằng chính $x$.
  • Do $x$ lên tới $10^{18}$, ta cần phân tích thừa số nguyên tố hiệu quả. Vòng lặp chỉ cần chạy đến $\sqrt{x}$.

Lỗi thường gặp

  • Sử dụng kiểu dữ liệu nhỏ hơn long long (ví dụ int)导致 overflow khi tính toán với $x$ lên tới $10^{18}$.
  • Quên kiểm tra ước nguyên tố cuối cùng sau vòng lặp (khi $x$ sau khi chia hết các ước nhỏ hơn vẫn còn > 1).
  • Xử lý sai với $x=1$ (mặc dù đề bài đảm bảo $x \ge 2$).
  • Logic trong Solution 3 có thể sai lệch nếu không kiểm tra kỹ các trường hợp biên (ví dụ khi $x$ là tích của hai số nguyên tố lớn). Solution 1 là lựa chọn an toàn nhất.

Bình luận

Please read the guidelines before commenting.


Không có bình luận tại thời điểm này.