---
# System prepended metadata

title: HSG 9 Đà Nẵng

---

# HSG 9 Đà Nẵng

## 2020 - 2021

### Bài 1: Số nguyên tố cân bằng

- **Tóm tắt đề bài**: Cho $N = xx...xyx...xx$ $(1 \le x \le 9, 0 \le y \le 9)$, đếm số lượng số $N$ thỏa $N$ là số nguyên tố.
- **Ý tưởng**: Sinh tất cả các số $N$ độ dài $k$ thỏa $N$ cân bằng và $N$ là số nguyên tố.

### Bài 2: Từ đại diện

- **Tóm tắt**: Cho một dãy xâu $S$ và một xâu $P$, đếm số lần $P$ xuất hiện trong $S$, trong $P$ có các dấu '?' là kí tự bất kì.
- **Ý tưởng**: Duyệt qua các xâu $X$ của $S$, nếu với mỗi kí tự của $X$ bằng mỗi kí tự của $P$ thì cộng 1 vào kết quả, nếu kí tự đang xét là '?' thì luôn cộng 1 vào kết quả.
- **Code tham khảo**:
```python=
# Nhập S
# Nhập P

ans = 0
for X in S:
    if length(X) != length(P):
        continue
    
    for i = 1 --> length(X):
        if X[i] == '?' or X[i] == S[i]:
            ans += 1
print(ans)
```

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

#define int long long
#define all(x) x.begin(), x.end()

signed main() {
    ios_base::sync_with_stdio(false); cin.tie(0);

    vector<string> S;
    string s; getline(cin, s);
    string cur = "";
    for(int i = 0; i < s.size(); ++ i) {
        if(s[i] == ' ') {
            S.push_back(cur);
            cur = "";
        }
        else cur += s[i];
    }
    S.push_back(cur);
    string P; cin >> P;

    int ans = 0;
    for(int i = 0; i < S.size(); ++ i) {
        if(S[i].size() != P.size()) continue;
        string p = P;
        for(int j = 0; j < P.size(); ++ j) 
            if(p[j] == '?')
                p[j] = S[i][j];
        if(S[i] == p) ++ ans;
    }
    cout << ans;
}
```

### Bài 3: Diện tích lớn nhất

- **Tóm tắt**: đọc đề <(")
- **Ý tưởng**: Cố định đoạn $MA$, có $AB$ tính $MB$ = $\sqrt{AB^2 - MA^2}$, đáp án $m = max(MA * MB)$
- **Code**:
```python=
ans = 0
for a = 1 --> k:
    b2 = k * k - a * a
    if b2 là số chính phương:
        ans = max(a * sqrt(b2), ans)
print(ans)
```

## 2021 - 2022

### Bài 1 & 2

### Bài 3: Tam giác
- Tóm tắt: Cho $a$, $b$ là 2 số nguyên dương. Đếm số số $c$ sao cho $a$, $b$, $c$ là 3 cạnh của một tam giác.
- $a + b > c > a - b$
- $(a + b) - (a - b) - 1 = 2b - 1$ $(b < a)$

### Bài 4: Bội đặc biệt
- Tóm tắt: Cho số $P$ và $N$, và số $X$ có dạng $99...99$, đếm số số $X$ sao cho $X$ chia hết cho $P$ và $X$ có $N$ chữ số.
- Ví dụ: $P = 7$, $N = 7$ -> chỉ có $999999$ là thỏa mãn tính chất trên.

- **Subtask 1**: $P < 100, N \le 10$
Subtask này ta chỉ cần duyệt trâu qua toàn bộ các giá trị $X$ có thể.
```
X = X * 10 + 9
```
$X_{max} = 10^{10}$ $\Rightarrow$ ko sợ bị tràn số

- **Subtask 2**: $100 \le P \le 10000, N \le 160$
Subtask này ta cần biết 1 tính chất sau:
```
(a + b) % m = (a % m + b % m) % m
```
$X_{max} = 10^{160}$ $\Rightarrow$ ko ổn.
Sau khi tính toán, đáp án của mình là $X$ % $P$
Mình vừa tính, vừa mod luôn.
Áp dụng công thức trên:
```
X = (X * 10 + 9) % P (a = x * 10, b = 9)
  = (X * 10 % P + 9) % P
```
Sau khi tính xong, nếu $X = 0$ thì $ans + 1$.

- **Subtask 3**: $10^4 \le P \le 10^6$, $N \le 10^{18}$
- Giả sử ta thử $P = 7$, $N = 20$:
$9$: ko thỏa vì ko chia hết cho 7
$99$: như trên
$999$: như trên
$9999$: như trên
$99999$: như trên
$999999$: chia hết cho 7
.
.
.

- Ta nhận thấy, với test trên, chỉ có các số:
$999999$
$999999999999$
$999999999999999999$
thỏa mãn
- Thử $P = 7,8,9,...$, $N = 18$ để củng cố, khẳng định quy luật là đúng.


$\Rightarrow$ Ta có thể rút ra được quy luật: Gọi $L$ là độ dài của số nhỏ nhất thỏa mãn tính chất của đề
$\Rightarrow$ Đáp án sẽ là $\frac{N}{L}$ vì độ dài của những số sau sẽ tăng thêm $L$

## 2019 - 2020

### Bài 1: Đếm cặp đôi

- Tóm tắt: Có số $x$ ($x \le 10^6$), đếm trong dãy $A$ số cặp $(i, j)$ sao cho $A_i + A_j = x$
- **Cách 1: 2 con trỏ (two - pointers)**
***B1***: Sort lại mảng $A$
***B2***: Cố định 2 biến l, r ở 2 đầu của mảng $A$ ($l = 1, r = n$)
$A_l \le A_{l + 1}$ và $A_{r - 1} \le A_r$
***B3***: Check tổng $A_l + A_r$ so với $x$ $\Rightarrow$ 3 trường hợp có thể xảy ra:
*TH1*: $A_l + A_r < x$ $\Rightarrow$ tăng $l$ lên để tổng đến gần $x$ hơn.
*TH2*: $A_l + A_r > x$ $\Rightarrow$ giảm $r$ xuống.
*TH3*: $A_l + A_r == x$ $\Rightarrow$ $ans += (số lượng số $A_l$) * (số lượng số $A_r$).
- **Cách 2: dùng CTDL map (chỉ áp dụng cho C++)**
Khai báo:
```cpp=
map<int, int> mp; // gán cho mỗi giá trị int một giá trị int tương ứng
mp[3] = 2
mp[1000000000] = -1358374985729
mp[-1000] = 100
```
**Ưu điểm**: lưu bất kì giá trị nào mình muốn ($\le 10^{18}$)
**Nhược điểm**: truy cập giá trị trong mảng $O(1)$, truy cập map $O(log$ $n)$ ($n$ là kích thước hiện tại của map)

***B1***: tạo `map<int, int> cnt` để đếm phân phối
***B2***: với mỗi $A_i$, đếm số lượng $x - A_i$ trong mảng rồi cộng vào `ans`
***B3***: `cnt[A[i]] ++`

### Bài 3: Thừa số nguyên tố

- Tóm tắt: tự đọc đề
- **Solution**: đề bảo gì làm nấy <(")
Cụ thể hơn, ta đếm tổng số mũ của mỗi số $A_i$, rồi bỏ số có tổng số mũ lớn nhất

## 2018 - 2019

### Bài 3: Tìm phần thưởng

Code tham khảo (chưa tối ưu: $O(sqrt(m)) = O(10^8)$):
```cpp=
// nhap m
int ans = 0, max_diff = 0;
for(int a = 1 -> sqrt(m)) {
    if(m % a == 0) { // a la uoc cua m
        // a = q - x
        int b = m / i; // b = q + x
        int q = (a + b) / 2; // a + b = q - x + q + x = 2q --> q = (a + b) / 2
        int p = b - q + 1;
        if(q - p + 1 > max_diff) {
            max_diff = q - p + 1;
            ans = (p + q) / 2;
        }
    }
}
cout << ans;
```
Tối ưu: thay vì for từ $1$ đến $\sqrt{m}$ thì ta sẽ làm ngược lại, nếu tìm được ước thỏa mãn thì in ra luôn (vì cách này for từ ước lớn nhất $\Rightarrow$ nhỏ nhất $\Rightarrow$ nếu tìm được đáp án thỏa mãn thì chắc chắn đáp án đó là lớn nhất).


test test test test test test test 