Hướng dẫn giải của Lũy thừa của 3


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, Sengtraan, lephuochauhungvuong, nguyenbaanhkiet

Hiểu bài toán

Bài toán yêu cầu kiểm tra một số thực N có phải là lũy thừa của 3 với số mũ k là số nguyên hay không. Nếu có, in ra giá trị k, ngược lại in ra 'NO'. Với N trong khoảng [0, 10^18] và sai số cho phép epsilon <= 10^-15.

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

Cách Sử dụng Logarit và Làm tròn (Logarithmic Approach)
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;

int main() {
    long double N;
    cin >> N;

    if (N <= 0) {
        cout << "NO" << endl;
        return 0;
    }

    long double log_k = log10(N) / log10(3); // tính log3(N)
    long long k = llround(log_k);            // làm tròn về số nguyên

    long double power = pow(3.0L, k);        // 3^k
    if (fabsl(power - N) <= 1e-15) {
        cout << k << endl;
    } else {
        cout << "NO" << endl;
    }

    return 0;
}
  • Time Complexity: O(1)
  • Space Complexity: O(1)

Phương pháp này dựa vào tính chất logarit: Nếu N = 3^k thì log3(N) = k. Ta tính k = log10(N) / log10(3). Sau đó, ta làm tròn k về số nguyên gần nhất (llround). Cuối cùng, ta kiểm tra xem 3^k có bằng N trong khoảng sai số cho phép không. Nếu có, in ra k, ngược lại in 'NO'.

Cách Phương pháp Logarit với Kiểm tra Phân loại (Refined Logarithmic Approach)
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main()
{
long double n;
 cin >> n;
 long double k = round(log10l(n) / log10l(3) * 1e16) / 1e16;
 if (n >= 1 && ceil(k) == floor(k))
      cout << int(k);
 else if (0 < n && n < 1 && k < 0)
      cout << k;
 else
      cout << "NO\n";
      return 0;
}
  • Time Complexity: O(1)
  • Space Complexity: O(1)

Các giải pháp này cũng sử dụng logarit nhưng có kiểm tra điều kiện phức tạp hơn. Chúng tính k = log3(N) và kiểm tra nếu ceil(k) == floor(k) (tức là k là số nguyên) thì in ra. Tuy nhiên, cách xử lý các số âm (k < 0) và phân loại theo n >= 1 hoặc n < 1 là không cần thiết và có thể gây lỗi logic nếu k được tính toán sai lệch do làm tròn. Logic chính vẫn là tìm k sao cho 3^k ≈ N.

Cách Duyệt lũy thừa (Nếu N nhỏ)
// Pseudocode logic
long long p = 1;
int k = 0;
while(p <= N) {
    if (p == N) return k;
    p *= 3;
    k++;
}
return "NO";
  • Time Complexity: O(log_3(N)) ~ O(38)
  • Space Complexity: O(1)

Đối với N nhỏ (ví dụ N <= 10^18), ta có thể lặp từ 3^0, 3^1, ... tăng dần cho đến khi vượt quá N. Nếu tại bước nào đó giá trị bằng N thì trả về số mũ k. Phương pháp này chính xác tuyệt đối với số nguyên nhưng không phù hợp với số thực có sai số.

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

Cách tiếp cận Time Space Tên
1 O(1) O(1) Sử dụng Logarit và Làm tròn (Logarithmic Approach)
2 O(1) O(1) Phương pháp Logarit với Kiểm tra Phân loại (Refined Logarithmic Approach)
3 O(log_3(N)) ~ O(38) O(1) Duyệt lũy thừa (Nếu N nhỏ)

Bài học kinh nghiệm

  • Sử dụng công thức logarit để chuyển bài toán kiểm tra lũy thừa thành bài toán kiểm tra số nguyên của chỉ số k.
  • Cần sử dụng kiểu dữ liệu long double để đảm bảo độ chính xác cao nhất khi tính logarit và lũy thừa.
  • Kiểm tra lại bằng cách tính ngược giá trị 3^k và so sánh chênh lệch với N trong khoảng sai số epsilon.

Lỗi thường gặp

  • Lỗi làm tròn sai khi tính k từ logarit, dẫn đến việc chọn sai số mũ.
  • Quên xử lý trường hợp N = 0 (vì log(0) vô nghĩa và 3^k không bao giờ bằng 0).
  • Sử dụng sai kiểu dữ liệu (ví dụ: double thay vì long double) có thể gây sai số do mất mát bit khi tính toán với số lớn.

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.