---
# System prepended metadata

title: 'Đề TS10 - BRVT - 2021: Editorial'

---

# Đề TS10 - BRVT - 2021: Editorial

>[!Note] Thông tin
>Sau đây là lời giải của Kỳ thi Tuyển sinh 10 trường THPT chuyên Lê Quý Đôn tỉnh Bà Rịa - Vũng Tàu năm học 2021 - 2022.
>
>**Bạn đọc có thể nộp và chấm bài (test tự sinh)** [TẠI ĐÂY](https://chuyentin-brvt.online/contest/algi_ts10_15_24).
>
>:::info
>Chuẩn bị bởi ==team Logica==, thuộc khuôn khổ dự án [**The Algitect (ALGI Project)**](https://www.facebook.com/algitect.project)
>:::

[toc]

## Bài 1: 
### Tóm tắt đề bài
Cho số nguyên dương $n$. Yêu cầu tính:
$$
\begin {flalign}
S &= \frac{2}{3} + \frac{3}{4} + \cdots + \frac{n-1}{n} \\
T &= \frac{1}{3} - \frac{1}{3^2} + \frac{1}{3^3} - \cdots + \frac{1}{3^{2n-1}} - \frac{1}{3^{2n}}
\end {flalign}
$$

**Dữ liệu đảm bảo: $3 \le n \le 100$**.

### **Tính S**
Cho biến $i$ chạy vòng lặp từ $3$ đến $n-1$, cộng ==$\frac{i-1}{i}$== vào kết quả.

**Độ phức tạp thời gian:** $O\left(n\right)$.

### **Tính T**
>[!Tip]Nhận xét:
>$$
>\frac {1}{3^i} = \frac{1}{3^{i-1}} \cdot \frac{1}{3}
>$$

Với mỗi số hạng từ $1$ đến $2n$, duy trì một biến ==$cur = \frac{1}{3^i}$== là giá trị của số hạng đang xét, nếu ==$i$ lẻ==, cộng $cur$ vào đáp án, ngược lại trừ đáp án đi $cur$.

**Độ phức tạp thời gian:** $O\left(n\right)$.

### **Mã nguồn mẫu**

:::info
Độ phức tạp thời gian của cả bài toán là ==tổng độ phức tạp thời gian== của từng yêu cầu.
:::

:::success 
:::spoiler Code
```cpp = 1
#include <bits/stdc++.h>
using namespace std;

int main(){
    int n;
    cin >> n;
    
    double S = 0.0, T = 0.0;
    
    for (int i = 3; i <= n; i++) {
        S += 1.0 * (i-1) / i;
    }

    double cur = 1.0 / 3;
    for (int i = 1; i <= 2*n; i++) {
        if (i % 2 == 1 {
            T += cur;
        }
        else {
            T -= cur;
        }
        cur /= 3;
    }

    cout << fixed << setprecision(5) << S << '\n' << T;

    return 0;
}
```
:::

## Bài 2:

### Tóm tắt đề bài
Cho số nguyên dương $n$ và $a$, thực hiện các yêu cầu:
- Tìm số nguyên $k$ là số $n$ sau khi đã xóa đi tất cả các số $0$.
- Tìm chữ số tận cùng của $a^n$.

**Dữ liệu đảm bảo:** $2 \le n \le 10^{18}$ và $1 \le a \le 100$.

### **Yêu cầu 1**

Dùng cấu trúc dữ liệu [**vector**](https://blog.28tech.com.vn/stl-vector-trong-c), lần lượt tách và đưa từng chữ số sau cùng của $n$ (tức là $n \bmod 10$) vào vector (ngoại trừ các chữ số $0$). Sau đó, loại bỏ chữ số đó đi bằng cách chia $n$ cho $10$. 

### **Yêu cầu 2**

>[!Tip] Nhận xét
>Đáp an của bài toán là $a^n \bmod 10$.

Để tính $a^n \bmod 10$ một cách nhanh chóng, dùng [**lũy thừa nhị phân**](https://wiki.vnoi.info/algo/algebra/binary_exponentation.md) kết hợp với modulo trong lúc tính.

**Độ phức tạp thời gian:** $O\left( \log n \right)$

### **Mã nguồn mẫu**

:::info
Độ phức tạp thời gian của cả bài toán là ==tổng độ phức tạp thời gian== của từng yêu cầu.
:::

:::success 
:::spoiler Code
```cpp = 1
#include <bits/stdc++.h>
using namespace std;

long long n;
int a;

int Pow(int a, long long n) {
    if (n == 0) {
        return 1;
    }
    
    int tmp = Pow(a, n/2);
    tmp = tmp * tmp;
    if (n % 2 == 1) {
        tmp = tmp * a;
    }
    
    return tmp % 10;
}

int main(){
    cin >> n >> a;

    //Yêu cầu 1
    long long tmp = n;
    vector<int> digits;
    
    while (tmp > 0) {
        if (tmp % 10 != 0) {
            digits.push_back(tmp % 10);
        }
        tmp /= 10;
    }
    
    for (int i = digits.size()-1; i >= 0; i--) {
        cout << digits[i];
    }
    
    cout << '\n';

    //Yêu cầu 2
    cout << Pow(a, n);

    return 0;
}
```
:::

## Bài 3:

### Tóm tắt đề bài
Cho hai số nguyên dương $n$, $k$ và dãy số nguyên nguyên dương $A_1, A_2, A_3, \dots, A_n$. Thực hiện các yêu cầu:
- In ra các số $A_i$ có dạng $A_i = 5 \cdot h + 2 \left(h \in N \right)$.
- In ra số lượng số nguyên tố trong dãy.
- In ra khoảng cách lớn nhất giữa hai số chính phương (số lượng phần tử nhiều nhất nằm giữa hai số chính phương) trong dãy. In ra $-1$ nếu không tồn tại hai số chính phương.
- Chia dãy số thành hai phần sao cho: Phần thứ nhất có $k$ phần tử, phần thứ hai có $n-k$ phần tử. In ra độ chênh lệch lớn nhất giữa tổng của hai phần.

**Dữ liệu đảm bảo:** $2 \le k \le n \le 10^5$ và $1 \le A_i \le 10^7$.

### **Yêu cầu 1**

>[!Note]Nhận xét
>$$
>\begin {flalign}
>A_i &= 5h + 2 \\
>\Leftrightarrow 5h &= A_i - 2 \\
>\Leftrightarrow h &= \frac {A_i - 2}{5}
>\end {flalign}
>$$
> Mà $h$ nguyên, do đó $A_i - 2$ chia hết cho $5$, hay ==$\left(A_i - 2\right) \bmod 5 = 0$==.

Như vậy, ta duyệt qua dãy và in ra các số thỏa mãn điều kiện trên.

**Độ phức tạp thời gian:** $O \left(n\right)$.

### **Yêu cầu 2**

Vì $A_i \le 10^7$, có thể dùng kĩ thuật [**sàng nguyên tố**](https://wiki.vnoi.info/algo/algebra/prime_sieve.md), sau đó với mỗi số $A_i$ kiểm tra nhanh xem nó có phải số nguyên tố hay không và đếm.

**Độ phức tạp thời gian:** $O(n \log\log n)$.

### **Yêu cầu 3**

>[!Tip]Kiểm tra số chính phương
>:::success
>==Số chính phương== là bình phương của một số nguyên.
>:::
>Ta có thể kiểm tra một số $x$ có phải số chính phương hay không bằng cách lấy ==bình phương phần nguyên của $\sqrt x$== so sánh với ==$x$==.
>Nghĩa là, $x$ chính phương khi và chỉ khi:
>$$
>\left \lfloor \sqrt x \right \rfloor ^ 2 = x
>$$

Để khoảng cách giữa hai số chính phương được chọn là xa nhất, ta chọn số chính phương ==đầu tiên== và ==cuối cùng== của dãy.

**Độ phức tạp thời gian:** $O(n)$.

### **Yêu cầu 4**

>[!Note]Nhận xét: 
>Chênh lệch giữa hai phần là lớn nhất khi một phần đạt tổng lớn nhất có thể và phần còn lại phải nhỏ nhất có thể.
>
>Dễ dàng nhận thấy:
>- Nếu $k \ge n-k$, ta nhóm ==$k$ phần tử lớn nhất== chung với nhau và ==$n-k$ phần tử nhỏ nhất== chung với nhau
>- Nếu $k \lt n-k$, ta nhóm ==$n-k$ phần tử lớn nhất== chung với nhau và ==$k$ phần tử nhỏ nhất== chung với nhau 

Có thể tìm nhanh tập các phần tử lớn nhất hoặc nhỏ nhát bằng cách sắp xếp lại dãy số.

**Độ phức tạp thời gian:** $O(n \log n)$

### **Mã nguồn mẫu**

:::info
Độ phức tạp thời gian của cả bài toán là ==tổng độ phức tạp thời gian== của từng yêu cầu.
:::

:::success 
:::spoiler Code
```cpp = 1
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
const int A = 1e7 + 5;
int n, a[N], k, isPrime[A];

bool isSquare(int n) {
    int tmp = sqrt(n);
    
    if (tmp * tmp == n) {
        return 1;
    }
    
    return 0;
}

main(){
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    /// Yêu cầu 1
        for (int i = 1; i <= n; i++) {
            if ((a[i]-2) % 5 == 0) cout << a[i] << ' ';
        }
    
        cout << '\n';

    /// Yêu cầu 2
        isPrime[1] = 1;
        for (int i = 2; i*i <= 1e7; i++) {
            if (isPrime[i] == 0) {
                for (int j = i*i; j <= 1e7; j += i) {
                    isPrime[j] = 1;
                }
            }
        }

        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (isPrime[a[i]] == 0) {
                cnt++;
            }
        }
    
        cout << cnt << '\n';

    /// Yêu cầu 3
        int L = -1, R = -1;
        for (int i = 1; i <= n; i++) {
            if (isSquare(a[i])) {
                if (L == -1) L = i;
                R = i;
            }
        }
    
        if (L == R) {
            cout << -1 << '\n';
        }
        else {
            cout << R - L - 1 << '\n';
        }

    /// Yêu cầu 4
        sort (a+1, a+1+n);
        k = min(k, n-k);
    
        long long S_max = 0, S_min = 0;
        for (int i = 1; i <= n; i++) {
            if (i > k) {
                S_max += a[i];
            }
            else {
                S_min += a[i];
            }
        }
    
        cout << S_max - S_min;

    return 0;
}
```
:::

## Bài 4:

### Tóm tắt đề bài

Cho số nguyên $n$ và dãy số nguyên $A_1, A_2, A_3,\dots, A_n$.

**Yêu cầu:** Chia dãy $A$ thành hai phần sao cho ==tổng của mỗi phần đều là số nguyên tố== và ==chênh lệch giữa hai phần là nhỏ nhất==. In ra $-1$ nếu không có cách chia.

**Dữ liệu đảm bảo:** $2\le n \le 100$ và $1 \le A_i \le 10^4$.

### Lời giải

Áp dụng ==quy hoạch động cái túi==.

**Định nghĩa:** $F_i$ là khả năng tạo ra tổng bằng $i$ từ các phần tử trong dãy:
- $F_i = 1$, có thể tạo được tổng $i$.
- $F_i = 0$, không thể tạo được tổng $i$.

**Khởi gán:** $F_0 = 1$ (không sử dụng phần tử nào).

**Công thức:** Xét mọi phần tử $A_i$ và mọi tổng $j$ bất kỳ. Nếu $j - A_i \ge 0$ và $F_{j-A_i} = 1$ thì $F_j = 1$.

>[!Important] Lưu ý
>Ta tính $F_j$ dựa vào $F_{j-A_i}$ và $j - A_i \lt j$ nên cần duyệt $j$ giảm dần, để khi lấy $F_{j-A_i}$ ta đảm bảo đó là đáp án chưa được cập nhật bởi $A_i$.

>[!Tip]Nhận xét
>Gọi tổng của cả dãy số là $sum$.
>:::success
>Chênh lệch giữa hai phần là nhỏ nhất khi ==tổng của hai phần gần với $\frac {sum}{2}$== nhất.
>:::
>Trong hai phần, sẽ luôn có một phần với tổng $D \le \frac {sum}{2}$ và phần còn lại là $sum - D$ lớn hơn hoặc bằng $\frac {sum}{2}$.
>
> Không mất tính tổng quát, ta chỉ cần tính $D \le \frac {sum}{2}$. Đáp án tối ưu khi ==D lớn nhất==.

Đáp án hợp lệ khi ==$D$ và $sum - D$ là số nguyên tố==, ta có thể kiểm tra điều này bằng kĩ thuật [**sàng nguyên tố**](https://wiki.vnoi.info/algo/algebra/prime_sieve.md).

**Độ phức tạp thời gian:** $O(n \cdot \sum_{i=1}^n {A_i} + n \log \log n)$
> $n \cdot \sum_{i=1}^n {A_i}$: Quy hoạch động.
> $n \log \log n$: Sàng nguyên tố.

### **Mã nguồn mẫu**

:::success
:::spoiler Code
```cpp = 1
#include <bits/stdc++.h>
using namespace std;

const int N = 102 + 5;
const int S = 1e6 + 5;
int n, isPrime[S], a[N], f[S], sum;

int main(){
    isPrime[1] = 1;
    for (int i = 2; i*i <= 1e6; i++) {
        if (isPrime[i] == 0) {
            for (int j = i*i; j <= 1e6; j += i) {
                isPrime[j] = 1;
            }
        }
    }

    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
    }

    f[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1e6; j >= a[i]; j--) {
            f[j] = max(f[j], f[j - a[i]]);
        }
    }

    int res = -1;
    for (int i = 1; i <= sum/2; i++) {
        if (isPrime[i] == 0 && f[i] == 1 && isPrime[sum-i] == 0 && f[sum-i] == 1) {
            res = i;
        }
    }

    if (res == -1) {
        cout << -1;
    }
    else {
        cout << res << ' ' << sum - res << ' ' << sum - 2*res;
    }
    
    return 0;
}
```
:::