Flappy
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    1
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- title: '2SG TIN [Số học]' disqus: hackmd --- **Chuyên đề số học - Toán trong Tin** === ✍️ ***Author: 2School Guideline*** ## **Table of Contents** [TOC] --- ----- ## **Lời mở đầu** Hôm nay có một bạn chuyên Toán hỏi ban Tin 2SG: :::warning ##### - Các bạn chuyên Tin có cần biết Toán không? ::: Ban Tin của 2SG chỉ mỉm cười và đáp ngắn gọn như sau: :::success ##### - Chim thiếu ăn chim tìm con sâu. Tin thiếu Toán, Tin sầu con tim...! ::: :::info #### Bài viết này sẽ giúp các bạn thấy rõ hơn vẻ đẹp của sự kết hợp giữa Tin học và Toán học cũng như giới thiệu đến các bạn một vài chuyên đề số học cơ bản như sau: ::: | Số nguyên tố | | -------- | | **Sàng Eratosthenes** | | **Ước chung lớn nhất và bội chung nhỏ nhất** | | **Phân tích số nguyên** | ## **Số nguyên tố** --- ### 1. Khái niệm #### Số nguyên tố là số nguyên lớn hơn 1 và có đúng 2 ước là 1 và chính nó. **Ví dụ:** 5 là số nguyên tố vì 5 chỉ chia hết cho 1 và 5. Tuy nhiên, 6 là hợp số vì 6 chia hết cho 1, 2, 3 và 6. #### Nhận xét: - Số 2 là số nguyên tố chẵn duy nhất. - Cũng như tính chất của số nguyên dương, chúng ta chỉ tìm thấy số nguyên tố nhỏ nhất chứ không thể tìm thấy số nguyên tố lớn nhất. :::info **Từ định nghĩa trên, chúng ta sẽ có một vài thuật toán kiểm tra 1 số nguyên N có phải là nguyên tố hay không.** ::: ### 2. Thuật toán kiểm tra số nguyên tố ### A. Thuật toán "mầm non" Xét theo định nghĩa về số nguyên tố, ta có thể chạy vòng for từ $1$ đến $N$ để đếm số lượng ước của số nguyên dương $N$. Nếu $N$ có 2 ước thì $N$ là số nguyên tố, ngược lại $N$ không là số nguyên tố. #### Nhận xét chung :::info - Nếu $N < 2$ thì $N$ không là số nguyên tố. - Nếu $N \ge 2$ và $N$ có 2 ước thì $N$ là số nguyên tố. ::: #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; int n; bool check(int x) { if (x < 2) { return false; } int dem = 0; for (int i=1;i<=n;i++) { if (n%i == 0) { ++dem; } } if (dem == 2) { return true; } else { return false; } } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> n; if (check(n)) { cout << "TRUE"; } else { cout << "FALSE"; } } ``` #### Độ phức tạp của thuật toán: :::info Độ phức tạp của thuật toán là $O(N)$ do ta phải duyệt hết các số từ $1$ đến $N$. ::: ### B. Thuật toán “trưởng thành” #### **Giải thích:** **Xét số nguyên tố p** - Nếu $p$ lớn hơn $sqrt(n)$, và chúng ta xem xét bất kỳ bội số nào của $p$ lớn hơn $sqrt(n)$, ví dụ như $2p$, $3$, ..., thì mỗi bội số đó sẽ vượt quá giới hạn $n$ và không thể là số nguyên tố. - Điều này là do nếu $p$ lớn hơn căn bậc hai của $n$, thì $p * p$ cũng sẽ lớn hơn $n$. - Vì vậy, tất cả các bội số của $p$ lớn hơn $sqrt(n)$ sẽ vượt quá giới hạn $n$ và không cần phải kiểm tra nữa. #### **Chứng minh:** :::info **● Giả sử n là hợp số ⟹ ta có thể biểu diễn n = a × b với a, b là ước dương của n ● Giả sử: 1 ≤ a ≤ b ⟹ a × a ≤ a × b = n ⟹ 1 ≤ a ≤ $\sqrt{n}$ (với $a$ là $1$ ước dương của n). $\Rightarrow$ chỉ cần duyệt trong đoạn [2, $\sqrt{n}$]** ::: #### Ví dụ: Nếu chúng ta muốn kiểm tra xem số $17$ có phải là số nguyên tố hay không, thì chúng ta chỉ cần kiểm tra các ước số từ $2$ đến $sqrt(17) ≈ 4.12$. Trong trường hợp này, chúng ta chỉ cần kiểm tra ước số $2$ và $3$, không cần phải kiểm tra bất kỳ ước số nào lớn hơn căn bậc hai của $17$. #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; int n; bool check(int x) { if (x < 2) { return false; } for (int i=2;i<=int(sqrt(n));i++) { if (n % i == 0) { return false; } } return true; } int main() { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> n; if (check(n)) { cout << "TRUE"; } else { cout << "FALSE"; } } ``` #### Độ phức tạp của thuật toán: :::info Độ phức tạp của thuật toán là $O(\sqrt{N})$. ::: ### **Kết luận** :::success **Thuật toán “trưởng thành” sẽ là lựa chọn tốt nhất nếu bạn muốn kiểm tra số nguyên tố thay vì ngây thơ sử dụng thuật toán “mầm non”.** ::: ## **Sàng nguyên tố Eratosthenes** ### Giải thuật Sàng Eratosthenes dùng để tìm các số nguyên tố nhỏ hơn hoặc bằng số nguyên N nào đó. Nó còn có thể được sử dụng để kiểm tra một số nguyên N có phải là nguyên tố hay không. ![](https://hackmd.io/_uploads/H1mhdX9hh.gif) - **Bước 1:** Tạo 1 danh sách các số tự nhiên liên tiếp từ $2$ đến $(2, 3, 4,..., n)$. - **Bước 2:** Giả sử tất cả các số trong danh sách đều là số nguyên tố. Trong đó, $p = 2$ là số nguyên tố đầu tiên. - **Bước 3:** Tất cả các bội số của $p: 2p, 3p, 4p,...$ sẽ bị đánh dấu vì không phải là số nguyên tố. - **Bước 4:** Tìm các số còn lại trong danh sách mà chưa bị đánh dấu và phải lớn hơn $p$ và nhỏ hơn hoặc bằng căn bậc 2 của n . Nếu không còn số nào, dừng tìm kiếm. Ngược lại, gán cho p giá trị bằng số nguyên tố tiếp theo và quay lại bước 3. #### Khi giải thuật kết thúc, tất các số chưa bị đánh dấu trong danh sách là các số nguyên tố cần tìm. ### Code mẫu: ```cpp= void sieve(int N) { bool isPrime[N+1]; for(int i = 0; i <= N;++i) { isPrime[i] = true; } isPrime[0] = false; isPrime[1] = false; for(int i = 2; i * i <= N; ++i) { if(isPrime[i] == true) { // Mark all the multiples of i as composite numbers for(int j = i * i; j <= N; j += i) isPrime[j] = false; } } } ``` #### Độ phức tạp của thuật toán là: $O(N$ $log$ $log$ $N)$. ## Bài tập áp dụng ### Ví dụ #### Đề bài: [SỐ NGUYÊN TỐ TRONG ĐOẠN](https://vinhdinhcoder.net/Problem/Details/4606) #### Tóm tắt đề: Cho $2$ số nguyên dương $a, b$. Có bao nhiêu số nguyên tố trong đoạn $[a, b]$. **Dữ liệu nhập:** :::success - Dòng đầu tiên ghi số k là số các đoạn $[a, b]$ - Tiếp theo là $k$ dòng mỗi dòng ghi 2 số $a, b$. ::: **Kết quả:** :::success - Gồm $k$ dòng, dòng thứ $i$ ghi một số là số các số nguyên tố trong đoạn $[a, b]$ thứ $i$ đã cho. ::: **Ràng buộc:** - $1 ≤ a ≤ b ≤ 1.000.000$ - $1 ≤ k ≤ 100$ ##### Input ::: success 1 1 5 ::: ##### Output :::success 3 ::: **Giải thích** - Trong đoạn $[1..5]$ có $3$ số nguyên tố: $2, 3, 5$. :::spoiler #### Lời giải :::info - Sử dụng sàng nguyên tố Eratosthenes để đánh dấu các số nguyên tố trong đoạn $[1..1e6]$ thay vì xây dựng hàm kiểm tra nguyên tố và sử dụng lại nhiều lần. - Chạy vòng for từ $a$ đến $b$ để tìm ra và đếm số các số nguyên tố đã đánh dấu trong mảng nguyên tố đã sàng ở trên, sau đó xuất ra kết quả ::: :::spoiler #### Code tham khảo: ```cpp= #include <bits/stdc++.h> using namespace std; int a,b,dem,k; const int maxN = 1e6+5; bool isPrime[maxN]; void sieve(int N) { for(int i = 0; i <= N;++i) { isPrime[i] = true; } isPrime[0] = false; isPrime[1] = false; for(int i = 2; i * i <= N; ++i) { if(isPrime[i] == true) { // Mark all the multiples of i as composite numbers for(int j = i * i; j <= N; j += i) isPrime[j] = false; } } } int main () { ios_base::sync_with_stdio(false); cin.tie(NULL); cin >> k; sieve(maxN); for (int i=1;i<=k;i++) { cin >> a >> b; dem = 0; for (int j=a;j<=b;j++) { if (isPrime[j] == true) { dem++; } } cout << dem << endl; } return 0; } ``` ::: ### Một số bài tập khác: :::info https://vinhdinhcoder.net/Problem/Details/4605 https://vinhdinhcoder.net/Problem/Details/4606 https://vinhdinhcoder.net/Problem/Details/4625 ::: ## **Ước chung lớn nhất của hai số** --- ### 1. Định nghĩa :::info #### *[Ước chung lớn nhất](https://vi.wikipedia.org/wiki/%C6%AF%E1%BB%9Bc_s%E1%BB%91_chung_l%E1%BB%9Bn_nh%E1%BA%A5t) (GCD) hay (ƯCLN) của hai hay nhiều số nguyên là số nguyên dương lớn nhất là ước của tất cả các số đó.* ::: ### 2. Cách tìm GCD của hai số ### A. Thuật toán cơ bản Ta có nhận xét sau: $GCD(a,b) \le min(a, b) \le max(a, b) \le LCM(a,b)$ Vì thế ta có thể tìm $GCD$ của $a$ và $b$ bằng cách chạy từ $min(a, b)$ xuống $1$, giá trị đầu tiên là ước của cả $a$ và $b$ là $GCD$ cần tìm. #### Thuật toán có độ phức tạp là $O(min(a, b))$ #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b; cin >> a >> b; a = abs(a); b = abs(b); // đảm bảo các số đều là số dương if (a < b) swap(a, b); // mặc định a là giá trị lớn hơn, b là giá trị nhỏ hơn for (int i = b; i >= 1; --i){ if (a % i == 0 && b % i == 0){ cout << i; break; } } return 0; } ``` ### B. Thuật toán tối ưu Euclid Tuy nhiên, để tối ưu hơn, người ta thường dùng thuật toán Euclid để tìm $GCD$ của hai số. Thuật toán hoạt động dựa trên 2 tính chất: :::success - $GCD(a, 0) = GCD(0, a) = a$ với $a ≠ 0$ - Nếu $m$ là số nguyên bất kỳ, thì $GCD(a + m*b, b) = GCD(a, b)$ ::: Tính chất $1$ là trường hợp cơ sở của thuật toán Euclid, tính chất $2$ giúp ta chia nhỏ bài toán lớn, phức tạp thành bài toán nhỏ, dễ giải hơn. Theo đó, ta có thể lấy $a = a \% b$, hoán đổi hai số (vì số dư luôn bé hơn số chia) và lặp lại cho đến khi phần dư bằng $0$ thì ta sẽ tìm được $GCD$ của $a$ và $b$ :::info #### Độ phức tạp của thuật toán là $O(\log min(a, b))$ ::: ### Ví dụ ** **Yêu cầu:** Tính $GCD$ của $192$ và $78$. Trước hết, ta thấy $192 ≠ 0$ và $78 ≠ 0$ Ta có $192 \% 78 = 36$ ⇒ $GCD(192, 78) = GCD(78, 36)$ Ta lặp lại các bước cho đến khi phần dư bằng $0$: $78 \% 36 = 6 ⇒ GCD(78, 36) = GCD(36, 6)$ $36 \% 6 = 0 ⇒ GCD(36, 6) = GCD(6, 0)$ Khi phần dư nhận được là $0$, **số còn lại ($\ne 0$) là kết quả cần tìm.** #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; ll gcd(ll a, ll b) { if (b == 0) return a; return gcd(b, a%b); } int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b; cin >> a >> b; cout << gcd(a, b); return 0; } ``` ### C. Hàm tìm GCD trong thư viện C++ Thư viện `<algorithm>` có sẵn hàm `__gcd(a,b)` để tìm của $a$ và $b$ nên đa phần mọi người dùng hàm có sẵn thay vì tự cài thuật toán. ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b; cin >> a >> b; cout << __gcd(a, b); return 0; } ``` ## Bội chung nhỏ nhất của hai số ### 1. Định nghĩa :::info #### [Bội chung nhỏ nhất](https://vi.wikipedia.org/wiki/B%E1%BB%99i_s%E1%BB%91_chung_nh%E1%BB%8F_nh%E1%BA%A5t#:~:text=Trong%20s%E1%BB%91%20h%E1%BB%8Dc%2C%20b%E1%BB%99i%20s%E1%BB%91,cho%20c%E1%BA%A3%20a%20v%C3%A0%20b) (LCM) hay (BCNN) của hai số nguyên $a, b$ là số nguyên dương nhỏ nhất chia hết cho cả $a$ và $b$. ::: ### 2. Thuật toán tìm LCM của hai số ### A. Thuật toán cơ bản :::success Dựa trên định nghĩa, ta có thể tìm $LCM$ bằng cách cộng $max(a, b)$ cho chính nó cho đến khi tìm được một tổng chia hết cho số còn lại. ::: :::info **Trong trường hợp tệ nhất, thuật toán có độ phức tạp là $O(min(a,b))$** ::: #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b; cin >> a >> b; a = abs(a); b = abs(b); // đảm bảo các số đều là số dương if (a < b) swap(a, b); // mặc định a là giá trị lớn hơn, b là giá trị nhỏ hơn for (int i = 1; i <= b; ++i){ if (a*i % b == 0){ cout << a*i; break; } } return 0; } ``` ### B. Thuật toán cải tiến Ngoài ra, để tìm LCM của hai số, ta có thể áp dụng tính chất: :::info $GCD(a, b) * LCM(a, b) = a*b ⇔ LCM(a, b) = \frac{a*b}{GCD(a, b)}$ ::: :::success **Khi đó, độ phức tạp của thuật toán là $O(log (min(a, b)))$** ::: #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b; cin >> a >> b; cout << a*b/__gcd(a, b); return 0; } ``` Tuy nhiên công thúc này chỉ áp dụng được khi tìm LCM của 2 số và dễ bị tràn số trong một số trường hợp nên để tìm LCM của các số quá lớn hay nhiều số, ta cần phân tích các số ra thừa số nguyên tố rồi áp dụng công thức tính LCM đã học: ![](https://hackmd.io/_uploads/r1IMR4jn3.png) #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int n; vector<ll> a; map<ll, int> mp; void read(){ cin >> n; ll m; for (int i = 0; i < n; ++i){ cin >> m; a.push_back(abs(m)); // đảm bảo các số đều là số dương } } void factorise(ll num){ int cnt; for (int i = 2; i*i <= num; ++i) { cnt = 0; while (num % i == 0) { num /= i; ++cnt; } mp[i] = max(mp[i], cnt); // lưu số mũ có giá trị lớn nhất if (n == 1) break; } if (num != 1) mp[num] = max(mp[num], 1); } ll lcm(){ ll ans = 1; for (map<ll, int>::iterator it = mp.begin(); it != mp.end(); ++it) // duyệt qua các phần từ của map for (int j = 1; j <= it->second; ++j) ans *= it->first; // it->first lưu số nguyên tố. // it-> second lưu số mũ lớn nhất của số nguyên tố đó return ans; } int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); read(); for (int i = 0; i < n; ++i) factorise(a[i]); cout << lcm(); return 0; } ``` :::warning #### **Lưu ý:** Các số âm cần được đổi dấu trước khi áp dụng các thuật toán tìm GCD và LCM. ::: ## Bài tập áp dụng: ### Ví dụ: #### Đề bài: [Farmer And His Plot](https://www.codechef.com/problems/RECTSQ) #### Tóm tắt đề: :::warning Cho $m, n$ là độ dài cạnh của một hình chữ nhật. Ta chia hình chữ nhật ra thành nhiều hình vuông có diện tích bằng nhau, tính số hình vuông nhỏ nhất chia được. ::: :::spoiler #### Nhận xét: :::success - Số hình vuông chia được càng nhỏ thì diện tích của hình vuông càng lớn. - Diện tích các hình vuông bằng nhau $\Rightarrow$ cạnh của các hình vuông bằng nhau. - Toàn bộ hình chữ nhật được chia ra thành các hình vuông $\Rightarrow$ cạnh hình vuông phải chia hết cho m, n. $\Rightarrow$ Cạnh hình vuông là $GCD(m, n)$. Từ đó, ta có thể dễ dàng tìm ra số hình vuông nhỏ nhất chia được. ::: :::spoiler #### Code tham khảo: ```cpp= #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); ll a, b, c; int t; cin >> t; for (int i = 1; i <= t; ++i){ cin >> a >> b; c = __gcd(a, b); c *= c; // diện tích của mỗi hình vuông cout << a*b/c << "\n"; // số hình vuông chia được } return 0; } ``` ::: ### Một số bài tập khác: :::success [GCD and LCM](https://www.codechef.com/problems/FLOW016) [Apples and oranges](https://www.codechef.com/problems/APPLEORANGE) [Appy and Contest](https://www.codechef.com/problems/HMAPPY2) ::: ## **Phân tích số nguyên** ### 1. Khái niệm :::info Trong lý thuyết số, phân tích số nguyên là việc phân tách một hợp số thành một tích của các số nguyên nhỏ hơn. Nếu các số nguyên đó giới hạn lại chỉ là số nguyên tố, quá trình được gọi là phân tích số nguyên thành thừa số nguyên tố. ::: ### 2. Thuật toán #### A. Cách làm cơ bản #### Ý tưởng Ta sẽ chạy 1 vòng lặp gồm các số từ 2 đến $\sqrt{n}$ để thử hết tất cả những số có thể là thừa số nguyên tố và nếu số $n$ chia hết cho các số đó thì lặp lại quá trình đến khi kết thúc. Nếu $n$ không chia hết cho các số trong đoạn [2, $\sqrt{n}$] thì $n$ là thừa số nguyên tố cần tìm. :::success *Với cách tiếp cận này, độ phức tạp có được sẽ là: **$O(\sqrt{n})$*** ::: #### Code mẫu ```cpp= #include <bits/stdc++.h> using namespace std; int main() { ios_base::sync_with_stdio(0); cin.tie(0); int n; cin >> n; vector<int> v; // mảng động dùng để lưu trữ các thừa số nguyên tố của n for (int i=2; i*i<=n; ++i) { while (n % i == 0) { n/=i; v.push_back(i); // đẩy thừa số nguyên tố i vào trong vector } } if (n != 1) v.push_back(n); // nếu n > 1 nghĩa là n chính là thừa số nguyên tố còn lại cần tìm // in ra các thừa số nguyên tố đã tìm đc for (int t: v) cout << t << " "; return 0; } ``` #### B. Dùng sàng nguyên tố Eratosthenes: - **Nhận xét** - **Ưu điểm** của cách này là phân tích thừa số nguyên tố chỉ trong $O(\log(n))$ thay vì $O(\sqrt{n})$ thế nên sàng sẽ giúp ta phân tích được nhiều số nhỏ ra thừa số nguyên với thời gian chạy nhanh hơn cách A. - **Nhược điểm** là việc tiền xử lí sàng nguyên tố tốn độ phức tạp là $O(n*\log(n))$ như đã trình bày ở trên. Bên cạnh đó, để cài được sàng thì cần phải tạo được mảng có n phần tử, vì vậy những bài cho giới hạn $n$ quá lớn thì cách tiếp cận này không khả thi (ví dụ: $n > 3e8$). - **Ý tưởng**: - **Bước 1:** Dùng sàng nguyên tố để kiếm số nguyên tố nhỏ nhất là ước của từng số từ $2$ đến $n$ (vì tính chất của bài toán nên ta cần biến đổi sàng 1 chút so với thông thường). Sau đó duyệt lại 1 lần và gán thừa số nguyên tố nhỏ nhất của tất cả số nguyên tố là chính nó - **Bước 2:** Hàm phân tích thừa số nguyên tố của các số n được truyền vào hàm - **Bước 3:** In ra các kết quả cho từng truy vấn tương ứng #### Code mẫu: ```cpp= #include <bits/stdc++.h> using namespace std; // const int N = 1e6; vector <int> min_prime; // void eratosthenes_sieve() { for (int i=2; i*i <= N; ++i) { if (min_prime[i] == 0) // nếu i là số nguyên tố thì chọn i để thử làm thừa số nguyên tố nhỏ nhất của các số j for (int j = i*i; j <= N; j+=i) if (min_prime[j] == 0) min_prime[j] = i; // nếu min_prime[j] != 0 nghĩa là min_prime[j] đã có 1 thừa số nguyên tố nhỏ hơn giá trị "i" đang xét } // kiểm tra các số nguyên tố và gán thừa số nguyên tố nhỏ nhất là chính nó for (int i=2; i <= N; ++i) if (min_prime[i]==0) min_prime[i] = i; // nếu min_prime[i] = 0 => i chính là số nguyên tố } // vector <int> factorize(int n) // hàm để phân tích thừa số nguyên tố { vector <int> v; // mảng lưu trữ các thừa số nguyên tố của n while (n > 1) { v.push_back(min_prime[n]); // đẩy thừa số nguyên tố nhỏ nhất của giá trị "n" hiện tại vào vector n /= min_prime[n]; // chia n cho thừa số nguyên tố đó } return v; } // int main() { ios_base::sync_with_stdio(0); cin.tie(0); int query; cin >> query; // số lượng truy vấn min_prime.resize(N+1, 0); // gọi hàm sàng nguyên tố eratosthenes_sieve(); // while (query--) { int n; cin >> n; // vector <int> ans = factorize(n); // in ra các thừa số nguyên tố của n for (int t: ans) cout << t << " "; cout << "\n"; } return 0; } ``` - Với cách tiếp cận này, độ phức tập có được là: - $O(n*\log( \log(n)) + query*\log(n))$ - $n$: giá trị của các số được truy vấn - $query$: số lượng truy vấn - $n*\log(\log(n))$ cho việc tiền xử lí sàng nguyên tố, $query*\log(n)$ cho việc truy vấn và phân tích thừa số nguyên tố - **Tính chất thú vị:** :::warning **Nếu $N = p_1^{q_1}*p_2^{q_2}...*p_k^{q_k}$ với $p_1, p_2,...,p_k$ là các số nguyên tố thì $N$ có $(q_1+1)*(q_2+1)...*(q_k+1)$ ước phân biệt** ::: ## Bài tập áp dụng: ### Ví dụ: [CSES - Counting Divisors](https://cses.fi/problemset/task/1713/) ##### Đề bài :::warning - Cho $n$ số nguyên. Hãy đếm số ước của $n$ số nguyên $x$ ::: ##### Input :::success - Dòng đầu tiên chứa $n$ là số lượng số nguyên - Mỗi dòng trong $n$ dòng tiếp theo, nhập vào số nguyên $x$ ::: ##### Output :::success - Với $n$ dòng, mỗi dòng in ra số lượng ước của số tương ứng ::: ##### Giới hạn - $1 \le n \le 10^5$ - $1 \le x \le 10^6$ :::spoiler #### Code tham khảo: ```cpp= #include <bits/stdc++.h> using namespace std; signed main() { ios_base::sync_with_stdio(0); cin.tie(0); int n; cin >> n; while (n--) { int x; cin >> x; int ans = 1; // ans: số lượng ước của x // phân tích thừa số nguyên tố của x // áp dụng tính chất đã được nêu ra ở trên để đếm ước for (int i=2; i*i <= x; ++i) { if (x%i == 0) { int cnt = 0; // đếm số mũ của thừa số nguyên tố "i" while (x%i==0) { x/=i; cnt++; } ans *= (cnt+1); } } if (x > 1) ans *= 2; cout << ans << "\n"; } } ``` ::: #### Một số bài tập khác: :::success [k-Factorization (Codeforces)](https://codeforces.com/contest/797/problem/A) [Strongly Composite (Codeforces)](https://codeforces.com/problemset/problem/1823/C) [CHINUOC - ĐẾM SỐ CÓ 9 ƯỚC (Vinhdinhcoder)](https://vinhdinhcoder.net/Problem/Details/4640) ::: ## Tài liệu tham khảo: :::warning [Sàng Eratosthenes (Wikipedia)](https://vi.wikipedia.org/wiki/S%C3%A0ng_Eratosthenes) [VNOI Wiki - Số nguyên tố (Prime Numbers)](https://vnoi.info/wiki/translate/he/Number-Theory-2.md#s%E1%BB%91-nguy%C3%AAn-t%E1%BB%91-prime-numbers) [VNOI Wiki - phân tích thừa số nguyên tố bằng sàng Eratosthenes ](https://vnoi.info/wiki/translate/he/Number-Theory-2.md#ph%C3%A2n-t%C3%ADch-th%E1%BB%ABa-s%E1%BB%91-nguy%C3%AAn-t%E1%BB%91-v%E1%BB%9Bi-s%C3%A0ng-eratosthenes) [CP Handbook (Antti Laaksonen)](https://cses.fi/book/book.pdf) [Euclidean algorithms (Basic and Extended) (Geeksforgeeks)](https://www.geeksforgeeks.org/euclidean-algorithms-basic-and-extended/) [Khan Academy - The Euclidean Algorithm](https://www.khanacademy.org/computing/computer-science/cryptography/modarithmetic/a/the-euclidean-algorithm#:~:text=The%20Euclidean%20Algorithm%20for%20finding,%3D%20GCD(B%2CR)) ::: > [color=#9b46c9] Have a nice day. Bye! ###### tags: `tin trong toán` `2SG` `toán tin` `tin` `toán`

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully