HSG THCS Hải Phòng 2025-2026
SỞ GIÁO DỤC VÀ ĐÀO TẠO
THÀNH PHỐ HẢI PHÒNG
ĐỀ CHÍNH THỨC
(Đề thi gồm 05 bài; 04 trang)
KỲ THI CHỌN HỌC SINH GIỎI THÀNH PHỐ
Cấp THCS năm học 2025 - 2026
Môn thi: Tin học
Thời gian làm bài: 150 phút
Tổng quan đề thi
Phần tiêu đề “Tổng quan đề thi”Đối với các thí sinh sử dụng ngôn ngữ C++:
| Tên file nguồn nộp | Dữ liệu nhập từ | Kết quả in ra | Giới hạn bộ nhớ | Điểm | |
|---|---|---|---|---|---|
| Bài 1 | CBAI1.CPP | Bàn phím | Màn hình | 1024 MB | 5,0 |
| Bài 2 | CBAI2.CPP | Bàn phím | Màn hình | 1024 MB | 5,0 |
| Bài 3 | CBAI3.CPP | Bàn phím | Màn hình | 1024 MB | 6,0 |
| Bài 4 | CBAI4.CPP | Bàn phím | Màn hình | 1024 MB | 6,0 |
| Bài 5 | CBAI5.CPP | Bàn phím | Màn hình | 1025 MB | 8,0 |
Đối với các thí sinh sử dụng ngôn ngữ Python:
| Tên file nguồn nộp | Dữ liệu nhập từ | Kết quả in ra | Giới hạn bộ nhớ | Điểm | |
|---|---|---|---|---|---|
| Bài 1 | PBAI1.PY | Bàn phím | Màn hình | 1024 MB | 5,0 |
| Bài 2 | PBAI2.PY | Bàn phím | Màn hình | 1024 MB | 5,0 |
| Bài 3 | PBAI3.PY | Bàn phím | Màn hình | 1024 MB | 6,0 |
| Bài 4 | PBAI4.PY | Bàn phím | Màn hình | 1024 MB | 6,0 |
| Bài 5 | PBAI5.PY | Bàn phím | Màn hình | 1025 MB | 8,0 |
Hãy lập trình giải các bài toán sau:
Bài 1 (5 điểm)
Phần tiêu đề “Bài 1 (5 điểm)”Một số nguyên dương x được gọi là đẹp nếu như nó chia hết cho 5 và tổng các chữ số của nó cũng chia hết cho 5.
Yêu cầu: Cho dãy n số nguyên dương a1, a2, …, an. Hãy đếm xem có bao nhiêu số đẹp trong dãy trên.
Dữ liệu: Nhập từ bàn phím
- Dòng đầu chứa số nguyên dương n
- Tiếp theo là n dòng, dòng thứ i (i = 1, 2, …, n) chứa số nguyên dương ai. Tổng số lượng các chữ số của a1, a2, …, an không vượt quá 106
Kết quả: In ra màn hình một số nguyên duy nhất là số lượng số đẹp trong dãy số đã cho.
Ràng buộc:
- Có 80% số tests ứng với 80% số điểm của bài thoả mãn ai ≤ 109 ∀i = 1, 2, …, n
- Các tests còn lại không có ràng buộc bổ sung
Ví dụ:
| Dữ liệu | Kết quả |
|---|---|
515501402510 | 2 |
Giải thích: Chỉ có 2 số 50, 140 thoả mãn đồng thời hai điều kiện: chia hết cho 5 và tổng các chữ số cũng chia hết cho 5.
Bài 2 (5 điểm)
Phần tiêu đề “Bài 2 (5 điểm)”Sau n bài kiểm tra, điểm của Dũng được ghi lại thành dãy số nguyên a1, a2, …, an. Điểm này có thể âm (tương ứng với điểm phạt) nếu như lần kiểm tra đó Dũng gian lận hoặc sử dụng chat GPT. Thầy giáo muốn biết “giai đoạn tiến bộ nhất” mà Dũng thực hiện được, giai đoạn này là dãy các bài kiểm tra liên tiếp của Dũng có tổng điểm lớn nhất.
Yêu cầu: Hãy xác định tổng điểm của “giai đoạn tiến bộ nhất” mà Dũng thực hiện được.
Dữ liệu: Nhập từ bàn phím
- Dòng đầu tiên chứa số nguyên dương n (1 ≤ n ≤ 106)
- Dòng thứ hai chứa n số nguyên lần lượt là a1, a2, …, an (|ai| ≤ 109 ∀i = 1, 2, …, n). Hai số liên tiếp cách nhau bằng khoảng trống (space)
Kết quả: In ra màn hình một số nguyên duy nhất là kết quả tìm được.
Ràng buộc:
- Có 50% số tests ứng với 50% số điểm của bài có n ≤ 500
- 30% số tests tiếp theo ứng với 30% số điểm của bài có n ≤ 5000
- Các tests còn lại không có ràng buộc bổ sung
Ví dụ:
| Dữ liệu | Kết quả |
|---|---|
9-90 1 3 -2 5 -1 2 5 -3 | 13 |
Giải thích: Dãy điểm cần tìm là 1, 3, −2, 5, −1, 2, 5 có tổng 1 + 3 − 2 + 5 − 1 + 2 + 5 = 13
Bài 3 (6 điểm)
Phần tiêu đề “Bài 3 (6 điểm)”Cho dãy n số nguyên a1, a2, …, an và số nguyên dương M. Hãy đếm số lượng cặp (i, j) với 1 ≤ i < j ≤ n sao cho ai + aj chia hết cho M.
Dữ liệu: Nhập từ bàn phím
- Dòng đầu chứa hai số nguyên dương n, M (n ≤ 3 × 105; M ≤ 1018)
- Dòng thứ hai chứa n số nguyên lần lượt là a1, a2, …, an (|ai| ≤ 1018 ∀i = 1, 2, …, n)
Hai số liên tiếp trên cùng một dòng cách nhau bằng khoảng trống (space)
Kết quả: Ghi ra màn hình một số nguyên duy nhất là số cặp tìm được.
Ràng buộc:
- Có 40% số tests ứng với 40% số điểm của bài có n ≤ 5000
- 20% số tests tiếp theo ứng với 20% số điểm của bài có M ≤ 106
- Các tests còn lại không có ràng buộc bổ sung
Ví dụ:
| Dữ liệu | Kết quả |
|---|---|
5 41 3 2 6 2 | 4 |
Giải thích: Các cặp (i, j) tìm được là (1,2), (3,4), (3,5), (4,5).
Bài 4 (6 điểm)
Phần tiêu đề “Bài 4 (6 điểm)”Trước cửa nhà Dũng có n cây hoa hồng trồng thành một dãy và đánh số 1, 2, …, n từ trái qua phải. Dũng đánh giá “độ đẹp” của những bông hoa hồng trong cây hoa hồng thứ i bằng một số nguyên dương ai. Nhân ngày Quốc tế Phụ nữ (8/3), Dũng muốn làm 2 bó hoa tặng mẹ và tặng cô giáo chủ nhiệm bằng cách chọn mỗi cây hoa hồng không quá một bông hoa. Một bó hoa được gọi là đẹp nếu như “độ đẹp” của các bông hoa hồng trong bó hoa này chênh lệch nhau không quá K. Tất nhiên Dũng muốn tổng số bông hồng trong cả hai bó hoa càng lớn càng tốt.
Yêu cầu: Hãy tìm số lượng bông hồng lớn nhất có thể được chọn để làm 2 bó hoa.
Dữ liệu: Nhập từ bàn phím
- Dòng đầu tiên chứa hai số nguyên dương n, K (n ≤ 106; K ≤ 109)
- Dòng thứ hai chứa n số nguyên dương lần lượt là a1, a2, …, an (ai ≤ 109)
Hai số liên tiếp trên cùng một dòng cách nhau bằng khoảng trống (space)
Kết quả: Ghi ra màn hình một số nguyên duy nhất là tổng số lượng bông hoa tối đa trong hai bó hoa.
Ràng buộc:
- Có 30% số tests ứng với 30% số điểm của bài có n ≤ 10
- 20% số tests tiếp theo ứng với 20% số điểm của bài có n ≤ 100
- 20% số tests tiếp theo ứng với 20% số điểm của bài có n ≤ 5000
- Các tests còn lại không có ràng buộc bổ sung
Ví dụ:
| Dữ liệu | Kết quả |
|---|---|
6 51 2 4 7 7 13 | 5 |
Giải thích: Một cách để chọn 5 bông hoa cho 2 bó hoa là:
- Bó thứ nhất gồm 2 bông hoa lấy từ 2 cây hoa có “độ đẹp” 1, 4
- Bó thứ hai gồm 3 bông hoa lấy từ 3 cây hoa có “độ đẹp” 2, 7, 7
- Không có cách nào chọn 6 bông hoa hồng
Bài 5 (8 điểm)
Phần tiêu đề “Bài 5 (8 điểm)”Trường THCS nơi Dũng đang học có trồng một hàng cây xanh trông rất đẹp. Hàng cây gồm n cây xanh được đánh số thứ tự từ 1 đến n (theo hướng từ trái sang phải). Để đơn giản có thể coi hàng cây như trục toạ độ Ox và cây thứ i có toạ độ xi (x1 < x2 < ⋯ < xn).
Để tưới nước cho cây, nhà trường có kế hoạch lắp đặt m vòi tưới nước tự động. Vòi nước thứ i (i = 1, 2, …, m) được lắp tại vị trí cây ti, có bán kính tưới nước là Ri. Điều này có ý nghĩa rằng vòi nước này tưới được cây ti và tất cả các cây có khoảng cách đến ti không vượt quá Ri.
Yêu cầu: Cho biết vị trí lắp đặt m vòi nước và bán kính tưới nước của m vòi này. Hãy đếm xem có bao nhiêu cây được tưới nước.
Dữ liệu: Nhập từ bàn phím
- Dòng đầu tiên chứa hai số nguyên dương n, m (1 ≤ m ≤ n ≤ 106)
- Dòng thứ hai chứa n số nguyên x1, x2, …, xn (0 < x1 < x2 < ⋯ < xn ≤ 109) lần lượt là toạ độ của các cây 1, 2, …, n.
- Tiếp theo là m dòng, dòng thứ i chứa hai số nguyên dương ti, Ri (1 ≤ ti ≤ n; Ri ≤ 109) lần lượt là số hiệu và bán kính tưới nước của vòi nước thứ i (i = 1, 2, …, m).
Hai số liên tiếp trên cùng một dòng cách nhau bằng khoảng trống (space)
Kết quả: Ghi ra màn hình một số nguyên duy nhất là số cây được tưới nước.
Ràng buộc:
- Có 30% số tests ứng với 30% số điểm của bài có m = 1
- 30% số tests tiếp theo ứng với 30% số điểm của bài có n ≤ 2000
- Các tests còn lại không có giới hạn bổ sung
Ví dụ:
| Dữ liệu | Kết quả |
|---|---|
5 21 3 5 7 9 111 54 2 | 5 |
Giải thích: Vòi thứ nhất tưới được các cây số hiệu 1, 2, 3; vòi thứ hai tưới được các cây 3, 4, 5. Như vậy chỉ các cây 1, 2, 3, 4, 5 được tưới nước.
(Ví dụ được giữ nguyên như đề gốc: dòng đầu ghi n = 5 nhưng dòng thứ hai liệt kê 6 toạ độ.)
(Thí sinh không sử dụng tài liệu, cán bộ coi thi không cần giải thích gì thêm)
Hướng dẫn giải và đáp án
Phần tiêu đề “Hướng dẫn giải và đáp án”Bài 1 (5 điểm)
Phần tiêu đề “Bài 1 (5 điểm)”Xem hướng dẫn & lời giải
Hướng dẫn giải
Phần tiêu đề “Hướng dẫn giải”Số có thể rất dài nên xử lý dưới dạng xâu. Số chia hết cho 5 khi chữ số cuối là 0 hoặc 5; đồng thời tính tổng chữ số theo modulo 5. Đếm các số thỏa cả hai điều kiện.
Độ phức tạp: O(tổng số chữ số).
#include <bits/stdc++.h>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, ans = 0; cin >> n; while (n--) { string s; cin >> s; int mod = 0; for (char c : s) mod = (mod + c - '0') % 5; if ((s.back() == '0' || s.back() == '5') && mod == 0) ans++; } cout << ans;}Bài 2 (5 điểm)
Phần tiêu đề “Bài 2 (5 điểm)”Xem hướng dẫn & lời giải
Hướng dẫn giải
Phần tiêu đề “Hướng dẫn giải”Thuật toán Kadane: duyệt từ trái sang phải, bestEnd là tổng lớn nhất của đoạn con kết thúc tại vị trí hiện tại, bằng max(a[i], bestEnd + a[i]) (bắt đầu đoạn mới hoặc nối vào đoạn trước). Đáp án là bestEnd lớn nhất.
Độ phức tạp: O(n).
#include <bits/stdc++.h>using namespace std;using ll = long long;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; ll x, cur, best; cin >> x; cur = best = x; for (int i = 1; i < n; i++) { cin >> x; cur = max(x, cur + x); best = max(best, cur); } cout << best;}Bài 3 (6 điểm)
Phần tiêu đề “Bài 3 (6 điểm)”Xem hướng dẫn & lời giải
Hướng dẫn giải
Phần tiêu đề “Hướng dẫn giải”Đưa mỗi a[i] về phần dư r trong [0, M − 1] (chú ý số âm). Các phần tử đứng trước ghép được với r phải có phần dư (M − r) mod M. Cộng số lượng phần dư bù đã gặp rồi tăng tần suất của r.
Độ phức tạp: O(n) kỳ vọng.
#include <bits/stdc++.h>using namespace std;using ll = long long;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; ll M; cin >> n >> M; unordered_map<ll, ll> cnt; cnt.reserve(n * 2); long long ans = 0; for (int i = 0; i < n; i++) { ll x; cin >> x; ll r = x % M; if (r < 0) r += M; ll need = (r == 0 ? 0 : M - r); auto it = cnt.find(need); if (it != cnt.end()) ans += it->second; cnt[r]++; } cout << ans;}Bài 4 (6 điểm)
Phần tiêu đề “Bài 4 (6 điểm)”Xem hướng dẫn & lời giải
Hướng dẫn giải
Phần tiêu đề “Hướng dẫn giải”Sau khi sắp xếp, một bó tối ưu gồm các phần tử nằm trong một khoảng giá trị dài K. Tính len[r] là độ dài cửa sổ dài nhất kết thúc tại r và pref[r] là độ dài cửa sổ tốt nhất nằm trong tiền tố đến r. Ghép cửa sổ [l, r] với cửa sổ tốt nhất kết thúc trước l để hai bó không dùng chung bông.
Độ phức tạp: O(n log n).
#include <bits/stdc++.h>using namespace std;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n; long long K; cin >> n >> K; vector<long long> a(n); for (auto &x : a) cin >> x; sort(a.begin(), a.end()); vector<int> pref(n + 1); int l = 0, ans = 0; for (int r = 0; r < n; r++) { while (a[r] - a[l] > K) l++; int len = r - l + 1; ans = max(ans, len + pref[l]); pref[r + 1] = max(pref[r], len); } cout << ans;}Bài 5 (8 điểm)
Phần tiêu đề “Bài 5 (8 điểm)”Xem hướng dẫn & lời giải
Hướng dẫn giải
Phần tiêu đề “Hướng dẫn giải”Với vòi tại cây t, dùng lower_bound và upper_bound trên dãy tọa độ tăng để tìm đoạn chỉ số các cây nằm trong [x[t] − R, x[t] + R]. Đánh dấu đoạn bằng mảng hiệu; cuối cùng đếm các vị trí có tổng dồn dương.
Lưu ý: ví dụ của đề ghi n = 5 nhưng dòng thứ hai có 6 tọa độ; chạy với n = 6 thì chương trình cho đúng kết quả 5.
Độ phức tạp: O((n + m) log n).
#include <bits/stdc++.h>using namespace std;using ll = long long;int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int n, m; cin >> n >> m; vector<ll> x(n); for (ll &v : x) cin >> v; vector<int> diff(n + 1); while (m--) { int t; ll R; cin >> t >> R; --t; int l = lower_bound(x.begin(), x.end(), x[t] - R) - x.begin(); int r = upper_bound(x.begin(), x.end(), x[t] + R) - x.begin(); diff[l]++; diff[r]--; } int cur = 0, ans = 0; for (int i = 0; i < n; i++) { cur += diff[i]; if (cur > 0) ans++; } cout << ans;}