Bỏ qua để đến nội dung

HSG THCS Quảng Ninh 2025-2026

SỞ GIÁO DỤC VÀ ĐÀO TẠO TỈNH QUẢNG NINH ĐỀ THI CHÍNH THỨC

KỲ THI CHỌN HỌC SINH GIỎI CẤP TỈNH THCS NĂM 2026 Môn thi: Tin học - Ngày thi: 22/01/2026
Thời gian làm bài: 150 phút, không kể thời gian giao đề
(Đề thi này có 04 trang)


BàiTên bàiTệp chương trìnhTệp dữ liệuTệp kết quảBộ nhớThời gian / testĐiểm
1Tiền côngsal.*sal.inpsal.out1024 MB1 giây6,0 điểm
2Giá trịval.*val.inpval.out1024 MB1 giây6,0 điểm
3Dãy sốarr.*arr.inparr.out1024 MB1 giây5,0 điểm
4Số cặppai.*pai.inppai.out1024 MB2 giây3,0 điểm

Dấu * được thay thế bởi cpp hoặc py của ngôn ngữ lập trình được sử dụng tương ứng là C++ hoặc Python.

Hãy lập trình giải các bài toán sau:

Một công nhân tham gia một đợt làm thêm kéo dài liên tiếp trong d ngày. Ngày bắt đầu của đợt làm thêm này là ngày thứ k trong tuần. Tiền công làm việc hàng ngày được quy định như sau:

  • Các ngày từ thứ Hai đến thứ Sáu: Tiền công là a đồng mỗi ngày;
  • Các ngày thứ Bảy và Chủ Nhật: Tiền công là b đồng mỗi ngày.

Yêu cầu: Cho biết các giá trị k, d, a và b, hãy tính tổng số tiền công mà người công nhân đó nhận được sau khi kết thúc d ngày làm việc.

Dữ liệu vào: từ tệp văn bản sal.inp gồm một dòng duy nhất chứa 4 số nguyên k, d, a, b:

  • k là thứ bắt đầu (2 ≤ k ≤ 8, trong đó 2 là thứ Hai, 3 là thứ Ba, …, 8 là Chủ Nhật);
  • d là tổng số ngày làm việc (1 ≤ d ≤ 1000);
  • a, b là tiền công tương ứng (1 ≤ a, b ≤ 105).

Kết quả: ghi ra tệp văn bản sal.out một dòng duy nhất chứa một số nguyên là tổng số tiền công nhận được.

Các số trên một dòng của dữ liệu vào được ghi cách nhau bởi một dấu cách.

Ví dụ:

sal.inpsal.out
2 4 3 512
2 7 2 418
4 14 2 332
3 12 1 215

Ràng buộc:

  • Ràng buộc 1: 80% số test ứng với 80% số điểm của bài có 1 ≤ d ≤ 5, k = 2;
  • Ràng buộc 2: 20% số test ứng với 20% số điểm của bài có 6 ≤ d ≤ 1000.

Giải thích test 1: k = 2, d = 4, a = 3, b = 5 tức là công nhân bắt đầu làm từ thứ Hai và d = 4 nên sẽ làm liên tiếp các ngày thứ Hai, Ba, Tư và thứ Năm. Vì vậy tiền công là d * a = 4 * 3 = 12.

Cho dãy một gồm n số nguyên a1, a2, …, an. Một dãy con liên tiếp là dãy có dạng ai, ai+1, …, aj (1 ≤ i ≤ j ≤ n), j − i + 1 được gọi là độ dài của dãy con đó.

Yêu cầu: Hãy tìm độ dài lớn nhất của dãy con liên tiếp chỉ bao gồm đúng k giá trị.

Dữ liệu vào: từ file văn bản val.inp

  • Dòng đầu chứa hai số nguyên n, k (2 ≤ n ≤ 105; 1 ≤ k ≤ 2);
  • Dòng thứ hai chứa n số nguyên a1, a2, …, an (1 ≤ ai ≤ 3; i = 1, 2, …, n).

Kết quả: ghi ra file văn bản val.out một số nguyên duy nhất là độ dài lớn nhất của dãy con liên tiếp chỉ bao gồm đúng k giá trị.

Các số trên một dòng của dữ liệu vào được ghi cách nhau bởi một dấu cách. Dữ liệu đầu vào đảm bảo bài toán luôn tồn tại đáp án.

Ví dụ:

val.inpval.outGiải thích
8 1
1 1 1 2 2 2 2 2
5Dãy con thỏa mãn:
2 2 2 2 2
10 1
1 2 2 3 2 3 3 3 1 1
3Dãy con thỏa mãn:
3 3 3
10 2
1 3 2 3 3 1 1 3 1 2
6Dãy con thỏa mãn:
3 3 1 1 3 1

Ràng buộc:

  • Ràng buộc 1: 40% số test ứng với 40% số điểm của bài có k = 1; 1 ≤ ai ≤ 2 với mọi i = 1, 2, …, n và ai ≤ ai+1 với mọi i = 1, 2, …, n − 1;
  • Ràng buộc 2: 40% số test ứng với 40% số điểm của bài có k = 1;
  • Ràng buộc 3: 20% số test ứng với 20% số điểm của bài có k = 2.

Cho một dãy gồm n số nguyên a1, a2, …, an và các số nguyên b, k1, k2. Bạn được thực hiện tối đa k1 + k2 phép toán trên các phần tử của dãy để giảm tổng các phần tử của dãy số đã cho.

  • Phép toán loại 1: Chọn một số ai bất kì của dãy và thay thế số ai bằng số ⌊ai/2⌋ (chia đôi và lấy phần nguyên, ví dụ: ⌊17/2⌋ = 8). Bạn được sử dụng tối đa k1 phép toán loại 1.
  • Phép toán loại 2: Chọn một số ai bất kì của dãy và thay thế ai bằng giá trị lớn nhất của hai số ai − b và 0 (trừ đi b đơn vị, nếu kết quả âm thì lấy bằng 0). Bạn được sử dụng tối đa k2 phép toán loại 2.

Quy tắc: Với mỗi số ai trong dãy, bạn có thể chọn: không phép toán nào, chỉ phép toán loại 1, chỉ phép toán loại 2, hoặc dùng cả hai phép toán, mỗi phép toán được thực hiện tối đa 1 lần với số ai. Nếu dùng cả hai loại trên cùng một số, bạn có thể thực hiện theo thứ tự nào tùy ý.

Yêu cầu: Hãy tìm tổng nhỏ nhất của dãy số sau khi sử dụng tối đa k1 phép toán loại 1 và k2 phép toán loại 2.

Dữ liệu vào: từ tệp văn bản arr.inp

  • Dòng đầu chứa 4 số nguyên n, b, k1, k2 (1 ≤ n ≤ 300, 1 ≤ b ≤ 109, 0 ≤ k1, k2 ≤ n);
  • Dòng thứ hai chứa n số nguyên dương ai (1 ≤ ai ≤ 109).

Dữ liệu ra: ghi ra tệp văn bản arr.out

  • Một số nguyên duy nhất là tổng nhỏ nhất tìm được.

Các số trên một dòng của dữ liệu vào được ghi cách nhau bởi một dấu cách.

Ví dụ:

arr.inparr.out
7 4 2 0
1 2 1 8 3 5 7
19
7 4 2 1
1 2 1 8 3 5 7
15
7 9 4 5
19 2 1 8 8 5 5
1
7 9 4 4
8 8 8 8 8 8 8
12

Giải thích test 3: Thực hiện phép toán loại 1 với a1 thì a1 = ⌊19/2⌋ = 9. Tiếp theo thực hiện 5 phép toán loại 2 với các phần tử a1, a4, a5, a6, a7 ta được dãy mới: 0, 2, 1, 0, 0, 0, 0. Sau đó thực hiện 3 phép toán loại 1 với các phần tử a2, a3, a4 ta được dãy mới: 0, 1, 0, 0, 0, 0, 0 có tổng bằng 1.

Ràng buộc:

  • Ràng buộc 1: 30% số test ứng với 30% số điểm của bài có k2 = 0;
  • Ràng buộc 2: 30% số test ứng với 30% số điểm của bài có a1 = a2 = ⋯ = an;
  • Ràng buộc 3: 40% số test ứng với 40% số điểm của bài có n, k1, k2 ≤ 300, các giá trị đầu vào khác không có ràng buộc gì thêm.

Cho hai dãy gồm n số nguyên, dãy thứ nhất gồm các số a1, a2, …, an, dãy thứ hai gồm các số b1, b2, …, bn.

Yêu cầu: Hãy đếm số cặp chỉ số (i, j) (1 ≤ i ≤ j ≤ n) mà số lớn nhất của các số ai, ai+1, …, aj bằng số nhỏ nhất của các số bi, bi+1, …, bj.

Dữ liệu vào: từ tệp văn bản pai.inp

  • Dòng đầu tiên chứa số nguyên n (1 ≤ n ≤ 105);
  • Dòng thứ hai chứa n số nguyên a1, a2, …, an (1 ≤ |ai| ≤ 109);
  • Dòng thứ ba chứa n số nguyên b1, b2, …, bn (1 ≤ |bi| ≤ 109).

Kết quả: ghi ra tệp văn bản pai.out

  • Một dòng duy nhất là kết quả của bài toán.

Các số trên một dòng của dữ liệu vào được ghi cách nhau bởi một dấu cách.

Ví dụ:

pai.inppai.out
6
1 2 3 2 1 4
6 7 1 2 3 2
2
3
3 3 3
3 3 3
6
7
1 2 3 4 8 8 9
2 4 6 8 8 8 9
8
10
1 2 3 2 1 3 2 1 2 1
6 7 1 2 3 2 2 3 1 2
4

Ràng buộc:

  • Ràng buộc 1: 25% số test ứng với 25% số điểm của bài có n ≤ 100;
  • Ràng buộc 2: 25% số test ứng với 25% số điểm của bài có n ≤ 5000;
  • Ràng buộc 3: 25% số test ứng với 25% số điểm của bài có n ≤ 105, a1 ≤ a2 ≤ ⋯ ≤ an, b1 ≤ b2 ≤ ⋯ ≤ bn;
  • Ràng buộc 4: 25% số test còn lại ứng với 25% số điểm của bài không có ràng buộc gì thêm.

  • Thí sinh không được sử dụng tài liệu;
  • Giám thị không giải thích gì thêm.
Xem hướng dẫn & lời giải

Mã hóa thứ Hai đến Chủ nhật thành 0..6. Duyệt d ngày: ngày có chỉ số dưới 5 nhận a, hai ngày cuối tuần nhận b.

Độ phức tạp: O(d).

#include <bits/stdc++.h>
using namespace std;
int main() {
long long k, d, a, b;
cin >> k >> d >> a >> b;
long long ans = 0;
for (int i = 0; i < d; ++i) {
int day = (k - 2 + i) % 7;
ans += (day < 5 ? a : b);
}
cout << ans;
}
Xem hướng dẫn & lời giải

Dùng cửa sổ trượt với bảng đếm các giá trị. Mở rộng đầu phải; khi có quá k giá trị khác nhau thì thu đầu trái. Mỗi khi có đúng k giá trị, cập nhật độ dài lớn nhất.

Độ phức tạp: O(n).

#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, k;
cin >> n >> k;
vector<int> a(n), f(4);
for (int &x : a)
cin >> x;
int l = 0, distinct = 0, ans = 0;
for (int r = 0; r < n; ++r) {
if (f[a[r]]++ == 0)
++distinct;
while (distinct > k)
if (--f[a[l++]] == 0)
--distinct;
if (distinct == k)
ans = max(ans, r - l + 1);
}
cout << ans;
}
Xem hướng dẫn & lời giải

Quy hoạch động theo số phép loại 1 và loại 2 đã dùng: dp[x][y] là tổng nhỏ nhất của phần đã xét. Mỗi phần tử có bốn lựa chọn: không làm gì, chia đôi, trừ b, hoặc dùng cả hai (lấy kết quả nhỏ hơn giữa hai thứ tự). Chỉ cần lớp trước nên bộ nhớ O(k1 · k2).

Độ phức tạp: O(n · k1 · k2).

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = (1LL << 62);
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, K1, K2;
ll b;
cin >> n >> b >> K1 >> K2;
vector<ll> dp((K1 + 1) * (K2 + 1), INF), ndp;
auto id = [&](int x, int y) { return x * (K2 + 1) + y; };
dp[0] = 0;
for (int z = 0; z < n; ++z) {
ll a;
cin >> a;
ndp.assign(dp.size(), INF);
for (int x = 0; x <= K1; ++x)
for (int y = 0; y <= K2; ++y)
if (dp[id(x, y)] < INF) {
ll v = dp[id(x, y)];
ndp[id(x, y)] = min(ndp[id(x, y)], v + a);
if (x < K1)
ndp[id(x + 1, y)] = min(ndp[id(x + 1, y)], v + a / 2);
if (y < K2)
ndp[id(x, y + 1)] = min(ndp[id(x, y + 1)], v + max(0LL, a - b));
if (x < K1 && y < K2) {
ll both = min(max(0LL, a / 2 - b), max(0LL, a - b) / 2);
ndp[id(x + 1, y + 1)] = min(ndp[id(x + 1, y + 1)], v + both);
}
}
dp.swap(ndp);
}
cout << *min_element(dp.begin(), dp.end());
}
Xem hướng dẫn & lời giải

Với đầu trái l cố định, hàm F(r) = max(a[l..r]) − min(b[l..r]) không giảm theo r. Các đoạn cần đếm là các r có F(r) = 0. Dùng bảng thưa (sparse table) để truy vấn max/min trong O(1), rồi tìm kiếm nhị phân hai lần: vị trí đầu tiên F ≥ 0 và vị trí đầu tiên F > 0; hiệu hai vị trí là số đoạn hợp lệ với đầu trái l.

Độ phức tạp: O(n 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;
cin >> n;
int K = 1;
while ((1 << K) <= n)
++K;
vector<vector<ll>> mx(K, vector<ll>(n)), mn(K, vector<ll>(n));
for (ll &x : mx[0])
cin >> x;
for (ll &x : mn[0])
cin >> x;
for (int k = 1; k < K; ++k)
for (int i = 0; i + (1 << k) <= n; ++i) {
mx[k][i] = max(mx[k - 1][i], mx[k - 1][i + (1 << (k - 1))]);
mn[k][i] = min(mn[k - 1][i], mn[k - 1][i + (1 << (k - 1))]);
}
vector<int> lg(n + 1);
for (int i = 2; i <= n; ++i)
lg[i] = lg[i / 2] + 1;
auto diff = [&](int l, int r) {
int k = lg[r - l + 1];
return max(mx[k][l], mx[k][r - (1 << k) + 1]) - min(mn[k][l], mn[k][r - (1 << k) + 1]);
};
long long ans = 0;
for (int l = 0; l < n; ++l) {
int lo = l - 1, hi = n;
while (lo + 1 < hi) {
int m = (lo + hi) / 2;
if (diff(l, m) >= 0)
hi = m;
else
lo = m;
}
int first = hi;
lo = l - 1;
hi = n;
while (lo + 1 < hi) {
int m = (lo + hi) / 2;
if (diff(l, m) > 0)
hi = m;
else
lo = m;
}
ans += hi - first;
}
cout << ans;
}