Ước Số – Bội Số – UCLN – BCNN và Các Bài Toán Ứng Dụng
Nền tảng của số học, thuật toán và rất nhiều bài toán thi học sinh giỏi.
I. Mở đầu: Phép chia có dư
Khi làm việc với các bài toán ước số – bội số – UCLN, nền tảng đầu tiên ta cần nắm là phép chia có dư.
Mọi số nguyên dương a và b đều có thể viết:
a = k · b + r, với 0 ≤ r < b
Trong đó:
- k là thương của phép chia a cho b
- r là phần dư
Công thức này chính là nền tảng của thuật toán Euclid, dùng để tính UCLN cực nhanh.
II. Ước số
1. Khái niệm
Cho số nguyên dương N. Khi một số d thỏa mãn:
N % d = 0
Thì d được gọi là ước số của N.
2. Phân tích thừa số nguyên tố
Mọi số nguyên dương N đều có dạng:
N = pa × qb × rc × ...
Mọi ước của N sẽ có dạng:
px qy rz, với
0 ≤ x ≤ a, 0 ≤ y ≤ b, 0 ≤ z ≤ c
Số lượng ước của N:
(a + 1) (b + 1) (c + 1) …
100 = 22 × 52
Số ước = (2+1)(2+1) = 9.
III. Cách đếm số lượng ước của một số N
1. Cách 1 – Ngây thơ nhất: Duyệt 1 → N
Ý tưởng tự nhiên nhất: duyệt tất cả i từ 1 đến N và kiểm tra xem i có phải ước không.
- Dễ hiểu nhất
- Nhược điểm: nếu N lớn (109) thì vòng lặp chạy 1 tỉ lần → quá chậm
int main(){
long long n, cnt = 0;
cin >> n;
for(long long i=1;i<=n;i++)
if(n % i == 0) cnt++;
cout << cnt;
}
def count_divisors_naive(n):
return sum(1 for i in range(1, n+1) if n % i == 0)
2. Cách 2 – Nhận xét quan trọng: Ước xuất hiện theo cặp
Nếu i là ước của N thì N/i cũng là ước của N.
100 = 1×100 = 2×50 = 4×25 = 5×20 = 10×10
Mỗi ước ở trái đi kèm một ước ở phải.
Vì vậy ta chỉ cần duyệt từ 1 đến √N.
Độ phức tạp giảm từ O(N) → O(√N).
for(long long i = 1; i*i <= n; i++){
if(n % i == 0){
cnt++;
if(i != n/i) cnt++;
}
}
import math
def count_divisors(n):
cnt = 0
for i in range(1, int(math.sqrt(n)) + 1):
if n % i == 0:
cnt += 1
if i != n // i:
cnt += 1
return cnt
print(count_divisors(100)) # 9
3. Cách 3 – Tối ưu nhất: Dùng phân tích thừa số nguyên tố
Đây là phương pháp mạnh nhất khi cần xử lý N rất lớn (N ≤ 1012) hoặc cần trả lời nhiều truy vấn trong cùng một bài toán. Ý tưởng là phân tích N thành tích các thừa số nguyên tố rồi áp dụng công thức tính số lượng ước.
Ví dụ:
75600 = 24 × 33 × 52 × 71
Số ước = (4+1)(3+1)(2+1)(1+1) = 120 ước.
🔍 Tại sao đây là cách tối ưu nhất?
- Cách 1 (Duyệt 1 → N): O(N) → không thể chạy với N = 1012.
- Cách 2 (Duyệt 1 → √N): O(√N) ≈ 106 phép → vẫn tốt cho 1 truy vấn, nhưng nhiều truy vấn sẽ TLE.
- Cách 3 (Phân tích thừa số): Vẫn duyệt đến √N, nhưng số phép chia thực tế thấp hơn rất nhiều vì mỗi lần lấy được thừa số thì chia liên tục.
Đánh giá thực tế:
- N = 1012 → √N = 1.000.000
- Phân tích thừa số chỉ cần khoảng 100–300 phép chia
- → Nhanh hơn cách √N từ 5× đến 100×
📌 C++ – Hàm phân tích thừa số nguyên tố
#include <bits/stdc++.h>
using namespace std;
// Đếm số ước bằng phân tích thừa số
long long count_divisors(long long n) {
long long ans = 1;
for (long long p = 2; p * p <= n; p++) {
if (n % p == 0) {
int exp = 0;
while (n % p == 0) {
n /= p;
exp++;
}
ans *= (exp + 1);
}
}
if (n > 1) ans *= 2; // n còn lại là số nguyên tố
return ans;
}
int main() {
long long n;
cin >> n;
cout << count_divisors(n);
return 0;
}
📌 Python – Hàm phân tích thừa số + chương trình hoàn chỉnh
import math
def count_divisors(n):
ans = 1
p = 2
while p * p <= n:
if n % p == 0:
exp = 0
while n % p == 0:
n //= p
exp += 1
ans *= (exp + 1)
p += 1
if n > 1:
ans *= 2
return ans
# main
n = int(input())
print(count_divisors(n))
📘 Ví dụ minh hoạ độ tối ưu
Xét N = 999,983,000,000 (≈ 1012)
✔ Cách √N phải thử 1.000.000 số.
✔ Cách phân tích thừa số chỉ thực hiện khoảng 200 lần chia.
→ Nhanh gấp 5000 lần trong thực tế! → Đây là lý do tại sao các bài số học lớn (HSG – ICPC – OI) đều dùng phân tích thừa số.
IV. Ước số chung lớn nhất (UCLN)
1. Cách ngây thơ nhất: Duyệt từ min(a, b) về 1
Đây là cách tự nhiên nhất khi lần đầu học về UCLN. Ý tưởng: duyệt từ số lớn nhất có thể là ước chung (tức min(a,b)) rồi giảm dần.
- Duyệt d từ min(a,b) → 1
- Nếu d chia hết a và b thì đó là UCLN
- Dừng ngay khi gặp d đầu tiên
Nhược điểm:
Độ phức tạp O(min(a,b)). Nếu a, b khoảng 1012 thì thuật toán này bất khả thi.
#include <bits/stdc++.h>
using namespace std;
int gcd_naive(int a, int b) {
int limit = min(a, b);
for (int d = limit; d >= 1; d--) {
if (a % d == 0 && b % d == 0)
return d;
}
return 1; // nếu không có ước chung lớn hơn
}
int main() {
int a, b;
cin >> a >> b;
cout << gcd_naive(a, b);
return 0;
}
def gcd_naive(a, b):
limit = min(a, b)
for d in range(limit, 0, -1):
if a % d == 0 and b % d == 0:
return d
return 1
# main
a, b = map(int, input().split())
print(gcd_naive(a, b))
Ví dụ:
a = 12, b = 18
Duyệt: 12 → 11 → 10 → ... → 6 → dừng.
Kết quả: UCLN = 6.
2. Dùng phân tích thừa số nguyên tố
Dựa trên tính chất: nếu ta phân tích hai số thành thừa số nguyên tố, thì UCLN chính là tích của các thừa số chung, với số mũ nhỏ nhất giữa hai bên.
Ý tưởng:
- Phân tích a = p₁x₁ · p₂x₂ …
- Phân tích b = p₁y₁ · p₂y₂ …
- UCLN = p₁min(x₁, y₁) · p₂min(x₂, y₂) …
Cách này hiệu quả khi các số vừa phải (≤ 107), và thường được dùng khi ta cần tính thêm các tính chất khác như số ước, BCNN, phân tích số,… Nhưng với số rất lớn thì phân tích thừa số lại tương đối chậm.
Độ phức tạp: O(√N) cho mỗi lần phân tích.
Không tối ưu bằng Euclid (O(logN)) nhưng dễ hiểu và mạnh khi cần cấu trúc phân tích.
📌 C++ – Tính UCLN bằng phân tích thừa số
#include <bits/stdc++.h>
using namespace std;
// Hàm phân tích thừa số trả về map {prime -> exponent}
map<int,int> factorize(long long n) {
map<int,int> res;
for (long long i = 2; i * i <= n; i++) {
while (n % i == 0) {
res[i]++;
n /= i;
}
}
if (n > 1) res[n]++;
return res;
}
long long gcd_factor(long long a, long long b) {
auto fa = factorize(a);
auto fb = factorize(b);
long long g = 1;
for (auto &p : fa) {
int prime = p.first;
if (fb.count(prime)) {
int times = min(p.second, fb[prime]);
while (times--) g *= prime;
}
}
return g;
}
int main() {
long long a, b;
cin >> a >> b;
cout << gcd_factor(a, b);
return 0;
}
📌 Python – Tính UCLN bằng phân tích thừa số
def factorize(n):
res = {}
i = 2
while i * i <= n:
while n % i == 0:
res[i] = res.get(i, 0) + 1
n //= i
i += 1
if n > 1:
res[n] = 1
return res
def gcd_factor(a, b):
fa = factorize(a)
fb = factorize(b)
g = 1
for p in fa:
if p in fb:
times = min(fa[p], fb[p])
g *= p ** times
return g
# main
a, b = map(int, input().split())
print(gcd_factor(a, b))
Ví dụ:
a = 180 = 22 × 32 × 5
b = 84 = 22 × 3 × 7
→ UCLN = 22 × 31 = 12
Tại sao cách này mạnh?
- Dễ mở rộng cho nhiều số: 3 số, 4 số, …
- Dễ kết hợp với bài toán đếm ước, BCNN, tổng ước,…
- Cho ta thông tin đầy đủ về cấu trúc số
Tại sao không tối ưu?
- Phân tích thừa số O(√N) chậm hơn Euclid O(logN)
- Không phù hợp với số rất lớn (1012, 1015)
3. Cách tối ưu tuyệt đối: Thuật toán Euclid
Công thức nền tảng:
gcd(a, b) = gcd(b, a mod b)
Nhờ phép chia có dư, mỗi lần bài toán nhỏ đi rất nhanh.
Điều này nghĩa là: lấy số lớn chia cho số nhỏ, rồi thay số lớn bằng số nhỏ, thay số nhỏ bằng phần dư, tiếp tục cho đến khi dư = 0. Khi đó, số còn lại chính là UCLN.
Ví dụ minh họa:
Tính gcd(48, 18)
- 48 mod 18 = 12 → gcd(48,18) = gcd(18,12)
- 18 mod 12 = 6 → gcd(18,12) = gcd(12,6)
- 12 mod 6 = 0 → gcd(12,6) = 6
Kết luận: gcd(48,18) = 6
Độ phức tạp: O(log(min(a,b))).
Đây là cách nhanh nhất – dùng trong tất cả thư viện chuẩn của C/C++, Python, Java,…
📌 C++ – Thuật toán Euclid
#include <bits/stdc++.h>
using namespace std;
int gcd_euclid(int a, int b) {
while (b != 0) {
int r = a % b;
a = b;
b = r;
}
return a;
}
int main() {
int a, b;
cin >> a >> b;
cout << gcd_euclid(a, b);
return 0;
}
📌 Python – Thuật toán Euclid
def gcd_euclid(a, b):
while b != 0:
a, b = b, a % b
return a
# main
a, b = map(int, input().split())
print(gcd_euclid(a, b))
Tại sao Euclid nhanh nhất?
- Mỗi bước giảm b đáng kể, vì a % b luôn < b
- Số bước không vượt quá O(logN)
- Không cần phân tích thừa số (khó làm, chậm)
- Dùng được với số tới 1018, thậm chí số cực lớn trong BigInt
Ví dụ kiểm tra tốc độ:
Tính UCLN của 123456789 và 987654321:
gcd(123456789, 987654321) = 9
Mất khoảng vài micro-giây → nhanh hơn phân tích thừa số vài triệu lần.
4. Dùng hàm STL trong C++17
Từ chuẩn C++17, thư viện <numeric> đã bổ sung hai hàm rất mạnh:
- std::gcd(a, b) – tính UCLN siêu tối ưu
- std::lcm(a, b) – tính BCNN dựa trên gcd
Hai hàm này được viết bằng Assembly tối ưu và được kiểm thử đầy đủ → an toàn hơn tự cài đặt.
📌 C++ – Dùng std::gcd
#include <bits/stdc++.h>
#include <numeric> // chứa std::gcd. Đưa để giới thiệu, không cần viết
using namespace std;
int main(){
long long a, b;
cin >> a >> b;
long long g = gcd(a, b); // hoặc std::gcd(a, b)
cout << g;
}
📌 Giải thích sự tối ưu
- std::gcd được implement bằng Euclid nhưng tối ưu ở mức thấp (low-level optimized)
- Có xử lý tràn số, dấu âm, các trường hợp đặc biệt tốt hơn
- Kiểm thử bởi compiler vendor (GCC, Clang, MSVC)
- Được đánh giá nhanh hơn code tự viết trong phần lớn trường hợp
Ví dụ:
Input: 84 36
Kết quả: 12
📌 Python – Thư viện chuẩn tương tự (math.gcd)
Python cũng có hàm tối ưu sẵn:
import math a, b = map(int, input().split()) print(math.gcd(a, b))
Kết luận:
- Trong C++17 trở lên:
std::gcdnên dùng cho mọi bài thi - Không cần tự viết thuật toán Euclid (dù vẫn nên hiểu)
- Python: dùng
math.gcd– cũng rất tối ưu
V. Bội số chung nhỏ nhất (BCNN)
1. Khái niệm
BCNN (Least Common Multiple – LCM) của hai số nguyên dương a và b là số nhỏ nhất chia hết cho cả a và b.
Ví dụ:
a = 6, b = 8 → BCNN = 24
2. Cách ngây thơ: Duyệt bội của số lớn
Ý tưởng đơn giản nhất:
- Bắt đầu từ max(a,b)
- Duyệt từng bội: max(a,b), 2·max(a,b), 3·max(a,b), …
- Kiểm tra xem bội đó có chia hết cho cả hai số hay không
Nhược điểm:
- Nếu a và b lớn (ví dụ 109), sẽ phải duyệt rất nhiều → chậm
- Độ phức tạp O(LCM / max(a,b)) → có thể lên đến hàng tỷ phép chia
📌 C++ – Cách ngây thơ
long long lcm_naive(long long a, long long b){
long long x = max(a, b);
while(true){
if(x % a == 0 && x % b == 0)
return x;
x += max(a, b);
}
}
int main(){
long long a, b;
cin >> a >> b;
cout << lcm_naive(a, b);
}
📌 Python – Cách ngây thơ
def lcm_naive(a, b):
x = max(a, b)
while True:
if x % a == 0 and x % b == 0:
return x
x += max(a, b)
a, b = map(int, input().split())
print(lcm_naive(a, b))
Ví dụ:
a = 12, b = 15
Duyệt: 15, 30, 45, 60 → đúng sau 4 bước
3. Cách 2 – Dùng công thức: LCM = a·b / GCD
Dựa trên quan hệ giữa UCLN và BCNN:
LCM(a, b) = (a × b) / GCD(a, b)
Trong đó GCD được tính bằng Euclid – nhanh O(logN).
Ưu điểm:
- Cực nhanh – dùng trong mọi bài thi
- Tính được với số lớn (long long)
- Dễ cài đặt → chỉ vài dòng
📌 C++ – Công thức chuẩn
long long gcd(long long a, long long b){
while(b != 0){
long long r = a % b;
a = b;
b = r;
}
return a;
}
long long lcm(long long a, long long b){
return a / gcd(a, b) * b;
}
int main(){
long long a, b;
cin >> a >> b;
cout << lcm(a, b);
}
📌 Python – Công thức chuẩn
def gcd(a, b):
while b != 0:
a, b = b, a % b
return a
def lcm(a, b):
return a // gcd(a, b) * b
a, b = map(int, input().split())
print(lcm(a, b))
Ví dụ:
a = 21, b = 6
gcd = 3 → lcm = 21 × 6 / 3 = 42
4. Dùng hàm STL trong C++17
Từ C++17, thư viện <numeric> cung cấp std::gcd và std::lcm.
Ưu điểm:
- Độ chính xác tuyệt đối
- Đã được tối ưu tốt hơn code tự viết
- Gọn nhất – dùng một dòng
📌 C++ – std::gcd và std::lcm
#include <bits/stdc++.h>
using namespace std;
int main(){
long long a, b;
cin >> a >> b;
cout << lcm(a, b); // từ
}
Lưu ý: cần include <numeric> hoặc <bits/stdc++.h>.
Tổng kết:
- Cách ngây thơ: dễ hiểu, nhưng cực chậm → chỉ dùng minh họa
- Công thức LCM = a·b/GCD: nhanh nhất để tự code
- STL C++17: nhanh – gọn – an toàn
VI. BÀI TOÁN ỨNG DỤNG
1. Bài toán: Chọn 3 hộp quà – IOI Style
💡 Đề bài
Nhờ thành tích thi lập trình ấn tượng ở vòng loại IOI_VN, Jame đã được chọn vào đội tuyển dự thi IOI 202x và nhận được nhiều phần thưởng.
Ban tổ chức có N hộp quà, hộp quà thứ i có ghi số nguyên dương a[i] (các số a[i] đôi một khác nhau, biểu thị giá trị hộp quà đó).
Quy tắc chọn quà:
Jame được chọn 3 hộp quà bất kỳ. Giả sử chọn được bộ ba (a[i], a[j], a[k]).
Khi đó, ngoài tổng giá trị của 3 hộp quà, Jame còn nhận thêm một phần thưởng đặc biệt có giá trị X (đồng), trong đó:
X = ước chung nguyên tố lớn nhất
của gcd(a[i], a[j], a[k])
Nếu 3 số a[i], a[j], a[k] không có ước chung nguyên tố (tức
gcd(a[i], a[j], a[k]) = 1) thì tất nhiên X = 0.
Tổng số tiền Jame nhận được với bộ ba đó là:
S = a[i] + a[j] + a[k] + X
Yêu cầu: Hãy tính giá trị lớn nhất của S mà Jame có thể nhận được.
Ví dụ minh hoạ:
N = 4 Các hộp quà: 9, 11, 12, 15 Các cách chọn bộ 3: (9, 11, 12) gcd(9,11,12) = 1 → không có ước nguyên tố chung → X = 0 S = 9 + 11 + 12 + 0 = 32 (9, 11, 15) gcd(9,11,15) = 1 → X = 0 S = 9 + 11 + 15 + 0 = 35 (9, 12, 15) gcd(9,12,15) = 3 → ước nguyên tố lớn nhất là 3 → X = 3 S = 9 + 12 + 15 + 3 = 39 (lớn nhất) (11, 12, 15) gcd(11,12,15) = 1 → X = 0 S = 11 + 12 + 15 + 0 = 38 Kết luận: Cách chọn (9, 12, 15) cho S = 39 là tối ưu.
📥 Dữ liệu vào – Kết quả ra
Dữ liệu vào: tệp văn bản CHOOSGIF.INP
- Dòng 1: Ghi số nguyên dương
N(N ≤ 500). - Dòng 2: Ghi
Nsố nguyên dươnga[i](a[i] ≤ 105), đôi một khác nhau.
Kết quả: Ghi ra tệp văn bản CHOOSGIF.OUT một số nguyên là giá trị lớn nhất của S tìm được.
Ví dụ:
CHOOSGIF.INP 4 9 11 12 15 CHOOSGIF.OUT 39
💡 Hướng dẫn giải & Gợi ý tối ưu
1. Phân tích yêu cầu bài toán
Thực chất yêu cầu của bài toán chính là: cho N và dãy a[i], hãy chọn 3 phần tử sao cho:
S = a[i] + a[j] + a[k] + UCNT_max(a[i], a[j], a[k])
Trong đó UCNT_max(u, v, z) là ước chung nguyên tố lớn nhất của 3 số u, v, z, tức là thừa số nguyên tố lớn nhất của gcd(u, v, z).
2. Tìm UCLN của 3 số bằng giải thuật Euclid
Gọi u, v, z là 3 giá trị đã chọn. Ta có:
p = gcd(u, v, z) = gcd(u, gcd(v, z))
Ta dùng giải thuật Ơ-clit (Euclid) để tính gcd(v, z), rồi lại gcd(u, kết quả đó). Độ phức tạp mỗi lần là O(log(max(u, v, z))).
3. Phân tích p thành thừa số nguyên tố và lấy thừa số lớn nhất
- Nếu
p = 1thì 3 số không có ước chung nguyên tố →X = 0. - Nếu
p > 1, ta phân tích p thành tích các thừa số nguyên tố, thừa số nguyên tố cuối cùng sẽ là UCNT_max(u, v, z).
Điều này tương ứng với hàm largestPrime(p) đã nêu ở trên (hoặc phiên bản phantich(p) như trong code tham khảo).
4. Duyệt mọi bộ ba (i, j, k)
Ta thực hiện 3 vòng lặp:
Duyệt i từ 1 đến N-2
Duyệt j từ i+1 đến N-1
Duyệt k từ j+1 đến N
{
p = gcd(a[i], gcd(a[j], a[k]))
X = UCNT_max(a[i], a[j], a[k]) // ước chung nguyên tố lớn nhất
S = a[i] + a[j] + a[k] + X
Res = max(Res, S)
}
Vì N ≤ 500, số bộ ba là khoảng C(500,3) ≈ 20 triệu. Kết hợp với việc mỗi bước chỉ tính gcd và phân tích một số ≤ 105,
thuật toán vẫn chấp nhận được.
5. Mã C++ tham khảo
Dưới đây là chương trình C++ tham khảo, cài đặt đúng ý tưởng: sử dụng UCLN ba số, sau đó phân tích UCLN thành thừa số nguyên tố và lấy thừa số nguyên tố lớn nhất.
#include <bits/stdc++.h>
#define nmax 505
using namespace std;
int n, a[nmax], res = INT_MIN;
int c[nmax], t = 0;
int ucln(int u, int v)
{
int r = 0;
while (v > 0)
{
r = u % v;
u = v;
v = r;
}
return u;
}
void phantich(int u)
{
for (int i = 2; i * i <= u; i++)
while (u % i == 0)
{
c[++t] = i;
u /= i;
}
if (u != 1)
c[++t] = u;
}
int ucnt_max(int u, int v, int z)
{
int p = ucln(u, ucln(v, z));
if (p != 1)
phantich(p);
else
return 0;
return c[t]; // thừa số nguyên tố lớn nhất
}
void xuli()
{
for (int i = 1; i <= n - 2; i++)
for (int j = i + 1; j <= n - 1; j++)
for (int k = j + 1; k <= n; k++)
{
int s = 0;
s = a[i] + a[j] + a[k] + ucnt_max(a[i], a[j], a[k]);
if (res < s)
res = s;
}
}
int main()
{
freopen("CHOOSGIF.inp", "r", stdin);
freopen("CHOOSGIF.out", "w", stdout);
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
xuli();
cout << res;
return 0;
}
Trong thực hành, có thể tối ưu thêm bằng cách:
- Đặt lại biến
t = 0mỗi lần phân tích để không dùng chung mảngc[]giữa các lần gọi. - Hoặc dùng trực tiếp hàm
largestPrime(p)như phiên bản Python/C++ trình bày ở phía dưới.
6. Gợi ý tối ưu nâng cao
- Có thể lưu trước tất cả các số nguyên tố ≤ 105 bằng sàng Eratosthenes để phân tích nhanh hơn.
- Có thể thêm kiểm tra đơn giản: nếu
a[i] + a[j] + a[k]đã nhỏ hơn res hiện tại rất nhiều, có thể bỏ qua một số trường hợp (pruning).
📌 Hàm tìm thừa số nguyên tố lớn nhất của một số
def largest_prime(n):
last = 0
d = 2
while d * d <= n:
while n % d == 0:
last = d
n //= d
d += 1
return n if n > 1 else last
int largestPrime(int n){
int last = 0;
for (int d = 2; d * d <= n; d++){
while (n % d == 0){
last = d;
n /= d;
}
}
if (n > 1) last = n;
return last;
}
3. Bài toán: Tổng các số hoàn hảo
Đề bài:
Số hoàn hảo là số có tổng các ước số dương thực sự bằng chính nó. Các số hoàn hảo ≤ 10⁹ gồm:
- 6
- 28
- 496
- 8128
- 33550336
Cho một số nguyên dương N ≤ 10⁹.
Hãy đếm số cách phân tích N thành tổng các số hoàn hảo khác nhau.
Nếu không có cách nào → in ra -1.
Ví dụ:
Input: 34 Cách: 34 = 6 + 28 → 1 cách Output: 1 Input: 10 Không có cách phân tích → Output: -1
Phân tích:
Chỉ có 5 số hoàn hảo → chỉ cần duyệt tất cả 32 tập con (bitmask).
perf = [6, 28, 496, 8128, 33550336]
S = set()
for mask in range(1, 1 << 5):
s = sum(perf[i] for i in range(5)
if mask & (1 << i))
S.add(s)
int perf[5] = {6,28,496,8128,33550336};
set S;
for(int mask=1; mask < (1<<5); mask++){
long long s = 0;
for(int i=0; i<5; i++)
if(mask & (1<
4. Bài toán: Chọn quà chia hết cho c và d
Đề bài:
Có N món quà, đánh số từ 1 đến N. Một món quà hợp lệ nếu số thứ tự của nó chia hết cho cả c và d.
Hãy in ra số lượng món quà hợp lệ. Nếu không có món nào → in ra NONE.
Ví dụ:
Input: 20 2 5 Các số chia hết cho 2 và 5: 10, 20 → 2 món Output: 2 Input: 25 14 13 Không có số nào chia hết → Output: NONE
Phân tích:
Một số chia hết cho cả c và d ⇔ chia hết cho:
lcm(c, d)
Số lượng món hợp lệ:
⌊ N / lcm(c, d) ⌋
res = N // lcm(c, d) print(res if res > 0 else "NONE")
long long res = N / lcm(c, d); cout << (res ? to_string(res) : "NONE");
VII. TỔNG KẾT BÀI HỌC
- Nắm ước – bội – UCLN – BCNN
- Thực hành 3 cấp độ thuật toán đếm ước
- Thành thạo Euclid & Euclid mở rộng
- Áp dụng vào các bài toán HSG: chọn quà, số hoàn hảo, BCNN
- Biết cách triển khai song song Python & C++
VIII. BÀI TẬP TỰ LUYỆN
- Nhập a,b → in gcd(a,b), lcm(a,b) bằng cả C++ & Python.
- Dùng Euclid mở rộng tìm x,y sao cho ax+by=gcd(a,b).
- Đếm ước của N ≤ 10¹² bằng phân tích thừa số.
Tính Lũy Thừa Tích và ƯCLN- Tìm số nguyên tố lớn nhất chia hết cho gcd(a,b,c).
Học trò tri ân
☕ Một ly cà phê sẻ chiaBạn bè ủng hộ
🍜 Một bát phở ấm lòng
💳 Quét mã ủng hộ tuỳ tâm nhé!