Lời Giải HSG Tin Tỉnh Quảng Ninh (B) - 2024

November 4, 2025 August 16, 2024
Author Author Hung Nguyen Tuong

Bài 1. Doanh thu

Một nhà hàng buffet có nn khách hàng trong một ngày, mỗi khách có độ tuổi nhất định. Giá vé niêm yết là 300.000300.000 đồng/người, nhưng có chương trình khuyến mãi dựa trên độ tuổi:

  1. Nếu tuổi 5\le 5 → Miễn phí.
  2. Nếu 5<5 < tuổi 10\le 10 → Giảm 80%.
  3. Nếu 10<10 < tuổi 15\le 15 → Giảm 50%.
  4. Nếu tuổi 60\ge 60 → Giảm 20%.

Yêu cầu

Tính tổng doanh thu của nhà hàng trong ngày dựa trên chương trình khuyến mãi này.

Dữ liệu

File đầu vào: TURNO.INP

  • Dòng 1: Số nguyên n (1n107)n ~ (1 \le n \le 10^7) – Số lượng khách hàng.
  • Dòng 2: nn số nguyên a1,a2,an (1ai100)a_1,a_2\ldots,a_n ~(1 \le a_i \le 100) – Tuổi của từng khách hàng.
  • Các số cách nhau bởi dấu cách.

Kết quả

File đầu ra: TURNO.OUT

  • Ghi một số nguyên duy nhất – tổng doanh thu của nhà hàng.
  • Đơn vị mặc định là đồng.

Ví dụ

TURNO.INPTURNO.OUT
6
26 2 9 67 39 15
1050000

Ràng buộc

  • 60% số test: n103n\le 10^3.
  • 40% số test còn lại: n107n \le 10^7.

Lời giải

#include <bits/stdc++.h>
using namespace std;

int price(int a)
{
    if (a <= 5)
        return 0;
    if (a <= 10)
        return 60;
    if (a <= 15)
        return 150;
    if (a >= 60)
        return 240;
    return 300;
}

int main()
{
    int n, ans = 0;
    cin >> n;
    while (n--)
    {
        int a;
        cin >> a;
        ans += price(a);
    }
    cout << ans * 1000;
    return 0;
}

Bài 2. Dãy số

Để chuẩn bị cho kỳ thi học sinh giỏi cấp tỉnh năm nay, thầy Minh – giáo viên dạy Tin học của trường – muốn kiểm tra kiến thức lập trình của đội tuyển học sinh giỏi cấp tỉnh môn Tin học. Thầy ra một bài toán như sau:

Cho một dãy AA gồm nn số nguyên không âm a1,a2,,ana_1, a_2, \dots, a_n. Sau đó, biến đổi dãy AA thành dãy AA' sao cho phần tử thứ ii của dãy AA' có giá trị bằng tổng các giá trị từ phần tử thứ nhất đến phần tử thứ ii của dãy AA, với i=1,2,,ni = 1, 2, \dots, n.

Ví dụ, dãy AA55 phần tử là 0,1,0,2,20,1,0,2,2. Sau khi biến đổi, dãy AA'0,1,1,3,50,1,1,3,5.

Tiếp theo, cho một dãy BB gồm mm số nguyên không âm b1,b2,,bmb_1, b_2, \dots, b_m.

Yêu cầu

Hãy tìm vị trí xuất hiện của phần tử bj (1jm)b_j ~ (1 \leq j \leq m) trong dãy AA'.

Dữ liệu

File đầu vào: SEQ.INP

  • Dòng 1: Hai số nguyên n,m (1n,m106)n, m ~ (1 \leq n, m \leq 10^6) lần lượt là số lượng phần tử của dãy AA và dãy BB.
  • Dòng 2: nn số nguyên không âm a1,a2,,an (0ai106,1in)a_1, a_2, \dots, a_n ~ (0 \leq a_i \leq 10^6, 1 \leq i \leq n).
  • Dòng 3: mm số nguyên không âm b1,b2,,bm (0bj106,1jm)b_1, b_2, \dots, b_m ~ (0 \leq b_j \leq 10^6, 1 \leq j \leq m).

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

Kết quả

File đầu ra: SEQ.OUT

  • Một dòng gồm mm số nguyên, số thứ jj là vị trí xuất hiện đầu tiên của bj (1jm)b_j ~ (1 \leq j \leq m) trong dãy AA'.
  • Nếu có nhiều vị trí xuất hiện, in ra vị trí xuất hiện đầu tiên.
  • Nếu bjb_j không xuất hiện trong dãy AA', thì số thứ jj in ra 1-1.

Ví dụ

SEQ.INPSEQ.OUT
5 4
0 1 0 2 2
1 3 6 5
2 4 -1 5

Ràng buộc

  • 40% số test ứng với 40% số điểm của bài có n,m103n, m \leq 10^3.
  • 30% số test ứng với 30% số điểm của bài có n,m3×104n, m \leq 3 \times 10^4.
  • 30% số test còn lại ứng với 30% số điểm của bài có n,m106n, m \leq 10^6.

Lời giải

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
    int n, m;
    cin >> n >> m;
    ll a, b, p = 0;
    map<ll, int> mp;
    for (int i = 1; i <= n; i++)
    {
        cin >> a;
        p += a;
        if (mp.find(p) == mp.end())
            mp[p] = i;
    }
    while (m--)
    {
        cin >> b;
        cout << (mp.find(b) == mp.end() ? -1 : mp[b]) << " ";
    }
    return 0;
}
  • Độ phức tạp: O((m+n)logn)O((m+n)\log n).

Bài 3. Khảo cổ

Một nhóm khảo cổ tìm thấy những cổ vật có chứa thông tin được mã hóa theo quy tắc sau:

  • Dòng đầu tiên là số 00 hoặc số 11.
  • Dòng thứ hai là một dãy các ký tự chữ cái in thường (gọi là đoạn mã VV).

Qua nghiên cứu, họ phát hiện rằng thông tin trên cổ vật sau khi giải mã sẽ thu được một đoạn mã thông tin gốc SS. Quy luật biến đổi từ đoạn mã gốc SS về đoạn mã VV được thực hiện như sau:

  1. Nhân đôi đoạn mã gốc SS để tạo thành đoạn mã TT.
  2. Chọn một ký tự trong đoạn mã TT, nhân đôi ký tự đó, tạo thành đoạn mã UU (độ dài tăng thêm 1 ký tự).
  3. Nếu dòng đầu tiên trên cổ vật là 11, đảo ngược đoạn mã UU để nhận được đoạn mã VV. Nếu dòng đầu tiên trên cổ vật là 00, đoạn mã UU chính là đoạn mã VV.

Yêu cầu

Dựa vào thông tin trên cổ vật, xác định:

  • Ký tự đã được nhân đôi ở bước 2.
  • Đoạn mã thông tin gốc SS của cổ vật.

Dữ liệu

File đầu vào: ARCHA.INP

  • Dòng 1: Một số nguyên K (0K1)K~(0 \leq K \leq 1) – Quy tắc biến đổi (có đảo ngược hay không).
  • Dòng 2: Một đoạn mã VV – Chuỗi ký tự chữ cái in thường có độ dài không quá 10610^6.

Kết quả

File đầu ra: ARCHA.OUT

  • Dòng 1: Ký tự đã được nhân đôi ở bước 2.
  • Dòng 2: Đoạn mã gốc SS của cổ vật.

Ví dụ

ARCHA.INPARCHA.OUT
1
ebbaeba
b
abe
0
ttpcdtpcd
t
tpcd

Ràng buộc

  • 40% số test ứng với 40% số điểm, có độ dài của VV không quá 10210^2.
  • 30% số test ứng với 30% số điểm, có độ dài của VV không quá 10510^5.
  • 30% số test còn lại ứng với 30% số điểm, có độ dài của VV không quá 10610^6.

Lời giải

#include <bits/stdc++.h>
using namespace std;
#define nl '\n'

int k;
string V;
int cnt[26] = {0};

int main()
{
    cin >> k >> V;
    if (k)
        reverse(V.begin(), V.end());

    for (auto c : V)
        cnt[c - 'a']++;

    char duplicate = 'a';
    for (int i = 0; i < 26; i++)
    {
        if (cnt[i] & 1)
        {
            duplicate += i;
            break;
        }
    }

    int n = V.size();
    int m = (n - 1) / 2;

    int i = 0, j = m + 1, d = 0;
    while (i <= m && j < n)
    {
        if (V[i++] == V[j])
            j++;
        else
            d++;
    }
    cout << duplicate << nl;
    for (int i = (d <= 1 ? m + 1 : 0); i < (d <= 1 ? n : m); i++)
        cout << V[i];

    return 0;
}
  • Độ phức tạp: O(n)O(n).

Bài 4. Mê cung

Hai bạn AnBình cùng chơi trò chơi mê cung trong một công viên. Mê cung có dạng một bảng hình vuông kích thước n×nn \times n, trong đó:

  • Hàng được đánh số từ 1 đến nn (từ trên xuống dưới).
  • Cột được đánh số từ 1 đến nn (từ trái sang phải).
  • Ô tại hàng ii, cột jj chứa số nguyên aija_{ij}, với:
    • aij=1a_{ij} = 1 → Có cánh cửa thần kỳ, đưa người chơi đến hàng jj.
    • aij=0a_{ij} = 0 → Không có cánh cửa nào.

Mỗi hàng có ít nhất một cánh cửakhông có cánh cửa nào đưa người chơi đến chính hàng đó.

Luật chơi:

  • An bắt đầu ở hàng 11, Bình bắt đầu ở hàng nn.
  • Cả hai đồng thời chọn một ô có cánh cửa để đi đến một hàng khác.
  • Nếu không gặp nhau, mỗi người tiếp tục chọn một cánh cửa mới để di chuyển.
  • Quá trình này lặp lại cho đến khi hai bạn gặp nhau tại cùng một hàng.

Yêu cầu

Xác định số lần chọn ô có cánh cửa ít nhất để An và Bình gặp nhau tại một hàng trong mê cung.

Dữ liệu

File đầu vào: MAZE.INP

  • Dòng 1: Một số nguyên n (2n5×103)n~(2 \leq n \leq 5 \times 10^3) – Kích thước mê cung.
  • nn dòng tiếp theo, mỗi dòng chứa nn số nguyên (ai1,ai2,,ain)(a_{i1}, a_{i2}, \dots, a_{in}), mỗi số 00 hoặc 11, không có dấu cách giữa các số.

Kết quả

File đầu ra: MAZE.OUT

  • Ghi một số nguyên duy nhấtsố lần chọn ô có cánh cửa ít nhất để hai bạn gặp nhau.
  • Luôn có ít nhất một phương án để gặp nhau.

Ví dụ

MAZE.INPMAZE.OUTGiải thích
5
01000
00100
00010
00101
00010
2Lần 1: An chọn ô (1,2) có cánh cửa đưa đến hàng số 2. Bình chọn ô (5,4) có cánh cửa đưa đến hàng số 4.
Lần 2: An chọn ô (2,3) có cánh cửa đưa đến hàng số 3. Bình chọn ô (4,3) có cánh cửa đưa đến hàng số 3.
Hai bạn gặp nhau tại hàng số 3.

Ràng buộc

  • 60% số test ứng với 60% số điểm, có n102n \leq 10^2.
  • 30% số test ứng với 30% số điểm, có n2×103n \leq 2 \times 10^3.
  • 10% số test ứng với 10% số điểm, có n5×103n \leq 5 \times 10^3.

Lời giải

Ý tưởng chính là ta sẽ xét tất cả các hàng mà cả An và Bình đều có thể đi đến với ít nhất kk lần chọn cửa, và sau đó đưa ra hàng với giá trị kk nhỏ nhất.

Vậy ta sẽ cần tính toán với mỗi hàng i (1in)i ~ (1 \le i \le n), An cần mở ít nhất bao nhiêu cách cửa để đi từ hàng 1i1 \to i và Bình cần mở ít nhất bao nhiêu cách cửa để đi từ hàng nin \to i. Nếu số lần mở cửa để đi đến hàng ii của 2 bạn là như nhau thì ta sẽ xét hàng đó.

Vậy làm sao để biết An/Bình cần ít nhất bao nhiêu cánh cửa để đến ii?

Ta biết rằng, từ hàng ii có thể đi đến hàng jj nếu như hàng ii có cánh cửa tại vị trí jj.

Ta có thể đưa bài toán về dạng đồ thị và giải quyết một cách dễ dàng. Với ví dụ ở đề bài, ta có đồ thị như sau:

image.png

Mỗi đỉnh là biểu diễn một hàng, mỗi cạnh đại diện cho hàng ii có cửa đến hàng jj.

Vậy ta chỉ cần duyệt DFS 2 lần:

  • An: Bắt đầu từ đỉnh 11 đến các đỉnh khác.
  • Bình: Bắt đầu từ đỉnh nn đến các đỉnh khác.

Rồi sau đó so sánh để đưa ra kết quả là xong.

#include <bits/stdc++.h>
using namespace std;

const int inf = 1e9;
int n;
vector<vector<int>> adj;
vector<int> dAn, dBinh;

void bfs(int s, vector<int> &d)
{
    d.assign(n + 1, inf);
    vector<bool> visited(n + 1, false);
    queue<int> q;

    q.push(s);
    d[s] = 0;
    visited[s] = true;

    while (!q.empty())
    {
        int u = q.front();
        q.pop();

        for (int v : adj[u])
        {
            if (!visited[v])
            {
                visited[v] = true;
                d[v] = d[u] + 1;
                q.push(v);
            }
        }
    }
}

int main()
{
    cin >> n;
    adj.resize(n + 1);

    for (int i = 1; i <= n; i++)
    {
        string a;
        cin >> a;
        for (int j = 0; j < n; j++)
        {
            if (a[j] == '1')
                adj[i].push_back(j + 1);
        }
    }

    bfs(1, dAn);
    bfs(n, dBinh);

    int ans = inf;
    for (int i = 1; i <= n; i++)
    {
        if (dAn[i] != inf && dAn[i] == dBinh[i])
            ans = min(ans, dAn[i]);
    }

    cout << ans;
    return 0;
}
  • Độ phức tạp: O(n)O(n). Nhưng bước nhập dữ liệu đã là O(n2)O(n^2).