---
# System prepended metadata

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

---

# Đề TS10 - BRVT - 2020: 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 2020 - 2021.
>
>**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

Nhập vào một số nguyên dương $n$. 

**Yêu cầu**: Tính giá trị các biểu thức:
$$\begin {flalign}\
A &= \frac{1}{6}+\frac{2}{7}+\dots+\frac{2n-1}{2n + 4} 
\\
B &= \frac{1}{7}-\frac{2}{7^2}+\dots+\frac{2n+1}{7^{2n + 1}} 
\end {flalign}
$$

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

### **Tính biểu thức A**
- Duyệt $i$ từ $1$ đến $2n-1$, mỗi lần cộng vào đáp án $\frac{i}{i+5}$.
- **Độ phức tạp thời gian**: $O(n)$.
 
### **Tính biểu thức B**
- Duyệt $i$ từ $1$ đến $2n + 1$, mỗi lần cộng vào đáp nếu $i$ lẻ và trừ vào đáp án nếu $i$ chẵn một lượng là $\frac{i}{7^i}$
- Việc tính riêng tử và mẫu của $\frac{i}{7^i}$ là không khả thi do vấn đề về giới hạn lớn.
- Ta biến đổi $\frac{i}{7^i} = i \times \frac{1}{7^i}$. Gọi biến $p$ là giá trị $\frac{1}{7^i}$. Dễ dàng nhận thấy với mỗi lần chay ta chỉ cần gán lại giá trị cho $p = \frac p 7$ tương ứng với việc số mũ của $7$ tăng lên $1$ theo $i$.
- **Độ phức tạp thời gian**: $O(n)$.

>[!Note]
> Tuy $7^i$ là rất lớn, và lưu trữ bằng kiểu số thức sẽ dẫn đến ==sai số nghiêm trọng==, nhưng thực tế ta xét $\frac{1}{7^i}$, do đó sai số chỉ ở những ==hàng thập phân phía sau==. Vì vậy không ảnh hưởng đến kết quả.

:::danger
**Lưu ý**: Lưu đáp án bằng kiểu **số thực**.
:::

### **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 A = 0;
    for (int i = 1; i <= 2 * n - 1; i++) {
        A += (double)i/(i + 5);
    }

    double B = 0, p = 1;
    for (int i = 1; i <= 2 * n + 1; i++) {
        p = (double) p / 7;
        if (i % 2 == 1)
            B += ((double) i * p);
        else 
            B -= ((double) i * p);
    }
    
    cout << fixed << setprecision(5) << A << '\n' << B << '\n';

    return 0;
}
```
:::

## Bài 2:

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

Cho các số nguyên dương $a$, $b$, $c$, $k$, $x$, $y$.

**Yêu cầu**:
- Tính biểu thức $A = \frac{a \cdot b \cdot c}{k}$.
- Tính biểu thức $B = ((x + 1) \cdot (x + 2) \cdots (x + y)) ~mod~2020$.

**Dữ liệu đảm bảo:** $1 \le a, b, c, k \le 10^{18}$, $1 \le x \le 10^9$ và $1 \le y \le 10^6$.

### **Tính biểu thức A**

Nếu tính thông thường thì biểu thức $A$ sẽ tràn dữ liệu. Do đó cần xử lý số lớn để tính toán.

Ban đầu ta chuyển đổi giá trị $a$ thành một mảng số nguyên $A_i$ với $A_i$ là giá trị tại vị trí $i$ từ phải sang trái của số $a$.
> Ví dụ: $a = 123$ thì $A_1 = 3$.

Sau đó ta tính thông thường, nhân từng phân tử $A_i$ với $B$ từ $i = 1, 2, 3, \dots, n$ với $n$ là độ dài hiện tại của mảng $A_i$. Sau đó ta nhận lần lượt $A_i$ với $C$ tương tư như nhân với $B$.

Cuối cùng, dời $A_i$ lên $5$ vị trí, tức là $A_{i + 5} = A_i$, để khi chia ta lưu được $6$ số thập phân sau dấu phẩy trong $A_i$ với $i \in [0, 5]$.


>[!Tip] Nhận xét về độ dài số
>Độ dài tối đa của mảng $A$ bé hơn 100 vì độ dài giá trị của tích $10^{18} \times 10^{18} \times 10^{18}$ là $57$ sau đó ta thêm $5$ chữ số hàng thập phân thì tối đa độ dài đạt $62$.

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

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

Nếu chạy lần lượt $i$ từ $1$ đến $y$ để tính $(x + i)$ và nhân vào kết quả thì sẽ bị tràn dữ liệu.

Do đó, áp dụng tính chất chia lấy dư để xử lý tránh bị tràn số:
$$
(a \times b) \bmod c = [(a \bmod c ) \times (b \bmod c )] \bmod c
$$

**Độ phức tạp thời gian:** $O\left( y \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
Nguyễn Minh
#include <bits/stdc++.h>
using namespace std;

long long a, b, c, k, X, Y;
long long A[1000];
long long d[1000];

int main() {
    cin >> a >> b >> c >> k >> X >> Y;
    int B = 1;
    for (int i = 1; i <= Y; i++)
        B = B * ((X + i) % 2020) % 2020;

    int n = 0;
    while (a > 0)
    {
        int p = a % 10;
        a /= 10;
        n++;
        A[n] = p;
    }

    for (int i = 1; i <= 100; i++)
    {
        long long phu = A[i] * b + d[i]; // d[i] = tổng nhớ tại vị trí i.
        d[i] = 0;
        A[i] = phu % 10;
        phu /= 10;
        int j = i;
        while (phu > 0)
        {
            j++;
            int p = phu % 10;
            phu /= 10;
            d[j] += p;
        }
    }

    for (int i = 1; i <= 100; i++)
    {
        long long phu = A[i] * c + d[i];
        A[i] = phu % 10;
        phu /= 10;
        int j = i;
        while (phu > 0)
        {
            j++;
            int p = phu % 10;
            phu /= 10;
            d[j] += p;
        }
    }

    for (int i = 100; i >= 1; i--)
        if (A[i] != 0)
    {
        n = i; //Xác định độ dài hiện tại của mảng A[i]
        break;
    }

    for (int i = n + 5; i > 5; i--)
        {
            A[i] = A[i - 5]; // Dời giá trị lên 5 vị trí
            A[i - 5] = 0;
        }

    long long giatri = 0;
    for (int i = n + 5; i >= 0; i--)
    {
        giatri *= 10;
        giatri += A[i];
        A[i] = 0;
        if (giatri >= k)
        {
            A[i] = giatri / k;
            giatri = giatri % k;
        }
    }

    for (int i = n + 5; i >= 0; i--)
        if (A[i] != 0)
        {
            n = i; // xác định lại độ dài của mảng A[i]
            break;
        }

// Vì làm tròn đến số thập phân thứ 5 nên ta sẽ kiểm tra số thập phân thứ 6
    int q = 0;
    if (A[0] >= 5)
        q = 1;

    for (int i = 1; i <= n; i++)
        {
            A[i] += q;
            q = 0;
            if (A[i] == 10)
                {
                    A[i] = 0;
                    q = 1;
                }

            if (q == 0)
                break;
        }

    if (q == 1)
    {
        n++;
        A[n] = 1;
    }

    for (int i = n; i > 5; i--)
        cout << A[i];
    cout << ".";
    for (int i = 5; i >= 1; i--)
        cout << A[i];
    cout << "\n";

    cout << B;

    return 0;
}
```
:::

## Bài 3:

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

Cho dãy số nguyên gồm $n$ phần tử $a_1, a_2,\dots a_{n-1}, a_n$.

**Yêu cầu**: 
- In ra các phần tử của dãy có dạng $7 \times k$ ($k$ là số nguyên).
- Nếu tồn tại hai số nguyên âm nằm kề nhau in ra chữ số $1$, ngược lại in ra chữ số $0$.
- Tìm độ dài lớn nhất của đoạn con liên tiếp có các phần tử là số nguyên tố (nếu không tồn tại dãy có ít nhất $2$ phần tử in ra $0$).
- Tìm giá trị tuyệt đối nhỏ nhất của tổng hai phần tử bất kỳ.

**Dữ liệu đảm bảo:** $n \le 10^5$ và $|a_i| \le 10^7$.

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

Một số nguyên dương $x$ có dạng $7k$ khi ==$x \bmod 7 = 0$==.

Do đó, ta duyệt qua mọi phần tử và in ra các phần tử 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**

Duyệt qua mọi cặp số kề nhau và kiểm tra xem hai số có thỏa mãn hay không.

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

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

Sử dụng thuật toán [**sàng nguyên tố**](https://wiki.vnoi.info/algo/algebra/prime_sieve.md) để đánh dấu những giá trị nào là số nguyên tố.

Áp dụng ==quy hoạch động== cơ bản:
- **Định nghĩa:** $dp_i$ là độ dài đoạn con liên tiếp có nhiều phần tử nhất kết thúc ở vị trí $i$.
- **Công thức:**
    + Nếu $a_i$ là số nguyên tố, $dp_i = dp_{i - 1} + 1$.
    + Ngược lại, $dp_i = 0$.
- **Kết quả:** $max(dp_i)$.

**Độ phức tạp thời gian:** $O( n \log \log n)$.
> Độ phức tạp của sàng nguyên tố.

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

>[!Tip] Nhận xét
> Ta cần tìm tổng hai số trong dãy có giá trị gần bằng $0$ nhất.


Sắp xếp lại mảng $a$ theo thứ tự không giảm, sau đó áp dụng thuật toán ==hai con trỏ==:
- Con trỏ $i$ bắt đầu từ $1$.
- Con trỏ $j$ bắt đầu từ $n$.
- Tổng đang xét là tổng của $a_i$ và $a_j$.

Nếu tăng $i$ lên thì giá trị của tổng sẽ tăng lên, ngược lại, nếu giảm $j$ xuống thì giá trị của tổng sẽ giảm xuống. 

Chính vì tính chất đó, ta có thể di chuyển con trỏ tối ưu để tổng có thể gần bằng $0$ nhất và giá trị tuyệt đối của tổng là bé nhất.

**Độ 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;

int n;
long long a[1000005], dp[100005];
int vis[10000005];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
   
    for (int i = 1; i <= n; i++)
        if (a[i] % 7 == 0)
            cout << a[i] << " ";
    
    cout << "\n";

    bool kt = false;
    for (int i = 2; i <= n; i++)
        if (a[i] < 0 && a[i - 1] < 0)
        {
            kt = true;
            break;
        }
    
    if (kt == true) cout << 1;
    else cout << 0;
    cout << "\n";

    for (int i = 1; i <= n; i++)
        t[i] = 0;

    for (int i = 2; i * i <= 1e7; i++)
        if (vis[i] == 0)
            for (int j = i; j * i <= 1e7; j++)
                vis[j * i] = 1;
    vis[1] = 1;
    vis[0] = 1;

    long long ans = 0;
    for (int i = 1; i <= n; i++)
        if (vis[abs(a[i])] == 0 && a[i] > 0)
            {
                dp[i] = dp[i - 1] + 1;
                ans = max(ans, dp[i]);
            }
    if (ans <= 1)cout << 0;
    else cout << ans;
    cout << "\n";

    sort(a + 1, a + 1 + n);

    ans = 1e18;
    int j = n;
    for (int i = 1; i <= n; i++)
    {
        if (i > 1)
            ans = min(ans, abs(a[i] + a[i - 1]));
        if (j > 0)
            ans = min(ans, abs(a[i] + a[j]));
        while (abs(a[j]) > abs(a[i]) && j > i)
        {
            ans = min(ans, abs(a[i] + a[j]));
            j--;
        }
        if (j > 0)
            ans = min(ans, abs(a[i] + a[j]));
    }
    cout << ans;
    return 0;
}
```
:::

## Bài 4:

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

Cho một dãy $n$ số nguyên dương $a_i$ và một dãy $m$ số nguyên dương $b_i$. 

**Yêu cầu**: Tìm số lượng phần tử trong mảng $b_i$ có giá trị bằng tổng của ít nhất $1$ dãy con trong mảng $a_i$.

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

### Lời giải

>[!Tip] Nhận xét
> Bài toán yêu cầu kiểm tra có thể tạo được tổng $b_i$ bằng một tập con của $a$ không.

Áp dụng thuật toán ==quy hoạch động cái túi==

- **Định nghĩa:** $dp_{i,j}$ đánh dấu tổng $j$ có thể tạo được với $i$ số đầu tiên không:
    - $dp_{i,j} = 1$ nghĩa là tạo được tổng có giá trị bằng $j$.
    - $dp_{i,j} = 0$ là không tạo được.
- **Khởi gián:** $dp_{0, 0} = 1$.
- **Công thức:** $dp_{i,j} = 1$ khi $dp_{i-1, j - a_i} = 1$ hoặc $dp_{i-1, j} = 1$.

Duyệt qua $b_i$, nếu $dp_{n,b_i} = 1$ nghĩa là giá trị $s_b$ thỏa yêu cầu đề bài, ta tăng đáp án.

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

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

:::success
:::spoiler Code

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

const int N = 100, M = 1e4;
int dp[N+1][M+1], n, a[N+1];

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= 10000; j++) {
            dp[i][j] = dp[i-1][j];
            if (j >= a[i] && dp[i - 1][j - a[i]] == 1) {
                dp[i][j] = 1;
            }
        }
    }

    long long ans = 0;
    for (int i = 1; i <= m; i++) {
        if (dp[n][b[i]] == 1) {
            ans++;
        }
    }
    
    cout << ans;
    return 0;
}

```

:::