# Đề HSG9 - BRVT - 2015: Editorial
## Bài 1: Thừa kế (7 điểm)
Tính tổng các số chẵn và tổng các số lẻ trong đoạn $\left[ a, b \right]$ $\left( a, b \le 10^9 \right)$
:::info
:information_source: Ký hiệu toán học cần biết:
$\lceil \frac{a}{b} \rceil$: Phép tính $\frac{a}{b}$ làm tròn lên.
$\lfloor \frac{a}{b} \rfloor$: Phép tính $\frac{a}{b}$ làm tròn xuống
${\sum_{i=a}^b i}$ : Tổng các số từ a đến b
:::
### Brute - force:
Duyệt tuần tự từ a đến b và cộng các số chẵn hoặc lẻ vào tổng tương ứng.
Độ phức tạp thời gian: $O \left( b - a + 1 \right)$.
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int main() {
int sumOdd = 0, sumEven = 0;
for (int i = a; i <= b; i++) {
if (i % 2 == 0) {
sumEven += i;
}
else {
sumOdd += i;
}
}
if (sumEven > sumOdd) {
cout << 1 << "\n";
}
else {
cout << 2 << "\n";
}
cout << abs(sumEven - sumOdd);
return 0;
}
```
:::
### Accepted:
Xét đoạn $\left[ a, b \right]$, đặt:
- $sE$: Tổng các số chẵn.
- $sO$: Tổng các số lẻ.
- $sTotal$: Tổng các số.
Ta có ==$sO = sTotal - sE$==. Vậy chỉ cần tính **$sE$** và **$sTotal$**!
$$
\begin{flalign}
sTotal
&= \sum_{i=a}^b i \\
&= \sum_{i=1}^b i - \sum_{i=1}^{a-1} i \\
&= \frac{b(b+1)}{2} - \frac{(a-1)a}{2}
\end{flalign}
$$
>Tổng các số từ $a$ đến $b$ bằng tổng các số từ $1$ đến $b$ trừ đi tổng các số từ $1$ đến $a-1$
Những số chẵn trong đoạn $\left[ a, b \right]$ có thể viết lại thành $2i$ với $\lceil \frac{a}{2} \rceil \le i \le \lfloor \frac{b}{2} \rfloor$
$$
\begin{flalign}
sE
&= \sum_{i=\lceil \frac{a}{2} \rceil}^{\lfloor \frac{b}{2} \rfloor} 2i \\
&= 2 * \sum_{i=\lceil \frac{a}{2} \rceil}^{\lfloor \frac{b}{2} \rfloor} i \\
&= 2 * \left( \sum_{i=1}^{\lfloor \frac{b}{2} \rfloor} i - \sum_{i=1}^{\lceil \frac{a}{2} \rceil - 1} i \right) \\
&= 2 * \left( \frac{{\lfloor \frac{b}{2} \rfloor}({\lfloor \frac{b}{2} \rfloor}+1)}{2} - \frac{({\lceil \frac{a}{2} \rceil}-1){\lceil \frac{a}{2} \rceil}}{2} \right)
\end{flalign}
$$
>sE được tính bằng cách lấy tổng các số từ $\lceil \frac{a}{2} \rceil$ đến $\lfloor \frac{b}{2} \rfloor$ nhân đôi lên
Sau khi đã tính được $sE$ và $sTotal$, ta tính $sO$. Như vậy, bài toán đã được giải quyết!
>VD: Trong đoạn $\left[ 3, 10 \right]$ có những số chẵn sau: $4, 6, 8, 10$.
>Tương ứng với: $2*2, 2*3, 2*4, 2*5$.
>
>$sTotal = \frac{10*(10+1)}{2} - \frac{(3-1)*3}{2} = 52$
>
>${\lfloor \frac{a}{2} \rfloor} = {\lfloor \frac{10}{2} \rfloor} = 5$
>${\lceil \frac{b}{2} \rceil} = {\lceil \frac{3}{2} \rceil} = 2$
>
>$sE = 2 * \left( \frac{5*(5+1)}{2} - \frac{(2-1)*2}{2} \right) = 28$
>
>$sO = sTotal - sE = 52 - 28 = 24$
Độ phức tạp thời gian: $O \left( 1 \right)$.
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int main() {
long long a, b;
cin >> a >> b;
long long l = (a + 1) / 2, r = b / 2;
//Lấy max với 0 phòng trường hợp l = r
long long sE = max(0LL, (r * (r + 1) / 2 - l * (l - 1) / 2) * 2);
long long sTotal = b * (b + 1) / 2 - a * (a - 1) / 2;
long long sO = sTotal - sE;
if (sE > sO) {
cout << 1 << "\n";
}
else {
cout << 2 << "\n";
}
cout << abs(sE - sO);
return 0;
}
```
:::
## Bài 2: Chọn quà (7 điểm)
Cho dãy số nguyên dương $A_1, A_2, A_3, \dots, A_n$ $\left( n \le 10^6 \right)$.
Đếm số cách chọn cặp $A_i, A_j$ $\left( i < j \right)$ sao cho **tổng các số còn lại là chẵn**.
### Brute - force:
Tính trước tổng của dãy số và lưu vào biến $sum$.
Duyệt tuần tự để cố định $A_i$, tương ứng với mỗi giá trị lại duyệt tuần tự một lần nữa để tìm $A_j$. Kiểm tra nhanh cặp số hiện tại có thỏa mãn hay không bằng cách kiểm tra $sum - \left( A_i + A_j \right)$ có chẵn hay không.
Độ phức tạp thời gian: $O \left( b - a + 1 \right)$.
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int main() {
long long sum = 0;
for (int i = 1; i <= n; i++) {
sum += A[i];
}
int res = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
if ( (sum - (A[i] + A[j]) ) % 2 == 0) {
res++;
}
}
}
cout << res;
return 0;
}
```
:::
### AC:
Nhận thấy tính chẵn lẻ của $sum - (A_i + A_j)$ phụ thuộc vào tính chẵn lẻ của $(A_i + A_j)$ do $sum$ đã cố định, ta có thể chia ra các trường hợp như sau:
#### Trường hợp 1: $sum$ chẵn $\to$ $\left( A_i + A_j \right)$ phải chẵn.
Bài toán quy về **"Đếm số cặp số có tổng chẵn"**, chỉ có hai trường hợp như sau:
- Chẵn - Chẵn.
- Lẻ - Lẻ.
#### Trường hợp 2: $sum$ lẻ $\to$ $\left( A_i + A_j \right)$ phải lẻ.
Bài toán quy về **"Đếm số cặp số có tổng lẻ"**, chỉ có duy nhất trường hợp sau:
- Chẵn - Lẻ.
---
Như vậy để xử lý bài toán chỉ cần đếm số lượng số chẵn và số lẻ trong dãy.
Khi đó, đặt:
- $cnt_0$: số lượng số chẵn.
- $cnt_1$: số lượng số lẻ.
Ta có các công thức tính như sau:
- Số cặp chẵn - chẵn: $\frac {cnt_0 \cdot (cnt_0 - 1)}{2}$.
- Số cặp lẻ - lẻ: $\frac {cnt_1 \cdot (cnt_1 - 1)}{2}$
- Số cặp chẵn - lẻ: $cnt_0 \cdot cnt_1$
Độ phức tạp thời gian: $O \left( n \right)$.
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, x;
long long res = 0, sum = 0, cnt[2] = {0, 0};
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
sum += x;
cnt[x % 2]++;
}
if (sum % 2 == 0) {
cout << cnt[0] * (cnt[0] - 1) / 2 + cnt[1] * (cnt[1] - 1) / 2;
}
else {
cout << cnt[0] * cnt[1];
}
return 0;
}
```
:::
## Bài 3: Đổ nước vào chai (6 điểm)
Cho dãy số nguyên dương $A_1, A_2, A_3, ..., A_n$ $(n \le 10^5)$. Chọn một số phần tử sao cho không có hai phần tử nào liền kề nhau trong dãy, sao cho tổng các phần tử được chọn là **lớn nhất**.
### 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)$.
### AC:
Quy hoạch động cơ bản.
Định nghĩa $dp_i$ là tổng lớn nhất có thể đạt nếu xét lấy trong $i$ phần tử đầu tiên (có thể lấy hoặc không lấy).
Công thức quy hoạch động:
- $dp_1$ = $A_1$: Trường hợp cơ sở.
- $dp_i = \max(dp_{i-1}, \: dp_{i-2} + A_i)$ $\left( i \ge 2 \right)$:
- $dp_i = dp_{i-1}$: Không lấy $A_i$ vào tổng tối ưu.
- $dp_i = dp_{i-2} + A_i$: Lấy $A_i$ vào tổng tối ưu.
Kết quả: $dp_n$.
Độ phức tạp thời gian: $O \left( n \right)$.
:::spoiler Code
```cpp=1
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5;
int n, A[N+1];
long long dp[N+1];
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> A[i];
}
dp[1] = A[1];
for (int i = 2; i <= n; i++) {
dp[i] = max(dp[i-1], dp[i-2] + A[i]);
}
cout << dp[n];
return 0;
}
```
:::