# Đề HSG9 - BRVT - 2022: Editorial
## Bài 1: Đếm số ô trống (6 điểm)
Cho số nguyên dương $n$ $\left( n \le 10^{18} \right)$, đếm tổng số ô trống có trong các chữ số của $n$, biết mỗi chữ số có các ô trống như sau:
- Chữ số $8$ có $2$ ô trống.
- Chữ số $0, 4, 6, 9$ có $1$ ô trống
- Chữ số $1, 2, 3, 5, 7$ không có ô trống.
### Accepted:
Để thuận tiện, ta dựng sẵn mảng $d$ với ==$d_i$ là số ô trống của chữ số $i$.==
Sau đó ta ==duyệt qua từng chữ số== và cộng số ô trống vào đáp án.
Độ phức tạp thời gian: $O \left( D \right)$ với $D$ là số lượng chữ số của $n$.
:::success
:::spoiler Code
```cpp=1
#include<bits/stdc++.h>
using namespace std;
int n, d[10] = {1, 0, 0, 0, 1, 0, 1, 0, 2, 1};
int main() {
cin >> n;
int res = 0;
while (n > 0) {
res += d[n % 10];
n /= 10;
}
cout << res;
return 0;
}
```
:::
## Bài 2: Tam giác đều lớn nhất (7 điểm)
Có $n$ thanh gỗ có độ đài là $A_1, A_2, \dots, A_n$ $\left( n \le 10^5, \; A_i \le 10^9 \right)$. Hãy cho biết có bao nhiêu tam giác đều có **cạnh lớn nhất** có thể được tạo ra bằng cách ghép các thanh gỗ đã cho.
**Biết rằng:** nếu có $k$ thanh gỗ có cùng độ dài thì có thể tạo ra $\frac {(k-2) \cdot (k-1) \cdot k}{6}$ tam giác đều.
### Accepted:
Sử dụng kiểu dữ liệu [map](https://blog.28tech.com.vn/stl-map-trong-c), đặt ==$d_x$ là số lượng thanh gỗ độ dài $x$.==
> Không thể sử dụng mảng bình thường vì $x \le 10^9$.
Đặt $res$ là độ dài $x$ lớn nhất thỏa mãn có ít nhất ba thanh gỗ độ dài $x$ $\left( d_x \ge 3 \right)$.
Kết quả của bài toán là: ==$\frac {(d_{res}-2) \cdot (d_{res}-1) \cdot d_{res}}{6}$.==
Độ phức tạp thời gian: $O(n \log n)$.
> Vì mỗi thao tác trên **map** mất chi phí thời gian là $\log n$.
:::success
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int n;
map<int, int> d;
main(){
ios_base::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin >> n;
int res = 0;
for (int i = 1; i <= n; i++){
int x;
cin >> x;
d[x]++;
if (d[x] == 3) {
res = max(res, x);
}
}
long long k = d[res];
cout << k * (k-1) * (k-2) / 6;
return 0;
}
```
:::
## Bài 3: Aladdin (7 điểm)
Có $n$ cổ vật có giá trị là $A_1, A_2, \dots, A_n$ $\left( n \le 1000, \; A_i \le 10^9 \right)$. Tìm cách lấy các cổ vật để đạt tổng giá trị lớn nhất thỏa mãn quy tắc sau:
- Cổ vật lấy sau phải có **số thứ tự** lớn hơn cổ vật lấy trước.
- Cổ vật lấy sau phải có **giá trị** lớn hơn cổ vật lấy ngay trước và không được hơn quá $k$ đơn vị $\left( k \le 10^9 \right)$.
### Brute - force:
==Quay lui== thử tất cả các cách chọn.
Độ phức tạp thời gian: $O \left( 2^n \right)$.
### Accepted:
==Quy hoạch động cơ bản.==
Đặt $dp_i$ là tổng giá trị lớn nhất có thể nhận được nếu xét $i$ cổ vật đầu tiên và bắt buộc lấy cổ vật thứ $i$ ở vị trí cuối cùng.
Công thức quy hoạch động:
- Khởi gán: $dp_i$ = $A_i$ (chỉ lấy cổ vật thứ $i$).
- $dp_i = \max \left( dp_j \right) + A_i$ $\left( \forall \; j \lt i, \; A_j \le A_i \le A_j+k \right)$
Kết quả của bài toán: $\max(dp_i)$ $\left( 1 \le i \le n \right)$
Độ phức tạp thời gian: $O \left( n \right)$.
:::success
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
const int N = 1003;
int n, k, A[N];
long long dp[N], res;
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> A[i];
dp[i] = A[i];
for (int j = 1; j < i; j++) {
if (A[i] > A[j] && A[i] <= A[j]+k) {
dp[i] = max(dp[i], dp[j] + A[i]);
}
}
res = max(res, dp[i]);
}
cout << res;
return 0;
}
```
:::