---
# System prepended metadata

title: lời giải contest ITK23F1

---

# Lời giải contest ITK23F1

## SUMN

~~Không biết nên ghi gì...~~  
Thay vì nhập $N$ ở kiểu `int`, hãy nhập kiểu `string` rồi duyệt qua các chữ số của $N$ để cộng vào kết quả.  
~~Dễ quá nên không có ý tưởng viết sol, chắc không cần code mẫu đâu he~~  

## STRL

### Cách 1: ~~dành cho người lười~~
---
- Tạo 1 `set<char> se`.
- Duyệt qua xâu $S$, nhét hết các kí tự vào `se` (`se.insert(S[i])`).
- Kết quả là `se.size()`.

### Cách 2:
---
- Tạo một `vector<bool> check(256, false)` để kiểm tra kí tự $x$ có tồn tại trong xâu hay không.
- Duyệt qua tất cả các kí tự của xâu $S$, đặt `check[S[i]] = true`.
- Kết quả là số phần tử có giá trị `true` trong `check`.

## Chia kẹo
**quy ước $a < b < c < d$**
### với $k = 1$

kết quả đơn giản chỉ là $1$

### với $k = 2$

#### thuật 1 - $O(n)$
duyệt qua tất cả giá trị của $a$ khi đó $b = n - a$, nên chỉ cần kiểm tra thử $a < b$ không.

#### thuật 2 - O(1) - liên quan đến $k = 4$
gọi hàm $calc$ là cách tính số cặp có tông bằng $n$: `calc(n)`
- nếu $n \le 0$ thì kết quả là 0 (phục vụ $k = 4$)
- nếu $n$ chẳn thì $a \in [1..n/2-1]$ vì $a < b$ (n/2 - 1 vì để bỏ trường hợp $a = n/2 => b = n/2$) nên kết quả là $n/2 - 1$
- nếu $n$ lẻ thì sẽ không xuất hiện $a = b = n/2$ nên kết quả là $n/2$(làm tròn xuống)

### với $k = 3$
#### thuật $O(n^2)$
duyệt qua hết các trường hợp $a, b$ khi đó $c = n - a - b$, nên chỉ cần kiểm tra $b < c$

### với $k = 4$
#### thuật $O(n^2)$
- với biểu thức $a + b + c + d = n$, ta duyệt qua tất cả giá trị $a, b$
- biến đổi về $c + d = n - a - b$
- gọi $c = x + b, d = y + b$ để đảm bảo tính $c, d$ luôn lớn hơn $b$
- ta có biểu thức $x + b + y + b = n - a - b \Leftrightarrow x + y = n - a - 3 \times b$
- a, b đã được xác định bởi `2 for` lồng nhau $=> ans = ans + calc(n - a - 3 \times b)$
## Dãy con tăng dài nhất

### brute:  
#### hướng
Gọi $dp[i]$ là độ dài của dãy con tăng dài nhất kết thúc tại vị trí $i$.  
Ta có công thức truy hồi:  
$$
\begin{align*}
dp[1] &= 1 \\
dp[i] &= 1 + \max(dp[j]); \forall j \in [1, i-1], a[j] < a[i]
\end{align*}
$$
#### độ phức tạp: $O(n^2)$

### full:
#### hướng
Trong công thức ở trên, nếu ta gán $B(dp(i)) = A(i)$ thì ta sẽ có mảng $B$ với ý nghĩa: 

- $B(k)$ là giá trị kết thúc của dãy con tăng độ dài $k$ (tại thời điểm đang tính).

Có thể chứng minh rằng $B$ là dãy tăng. Giả sử đang duyệt đến vị trí $i$, đã tính được $dp(i) = k$. Ta gán $B(k) = A(i)$ như trên. Nếu có $B(k) \geq B(k + 1)$ thì ta đã tính $dp(i)$ sai, vì trước $A(i)$ có một phần tử $A(j) \leq A(i)$ mà lại có $dp(j)$ lớn hơn $dp(i)$. Tương tự, cũng có thể thấy $B(k - 1) \geq B(k)$ là điều vô lý.

Từ nhận xét $B$ là dãy tăng, ta sử dụng tìm kiếm nhị phân để tìm dãy con tăng dài nhất trong $O(N \log N)$ như sau:

1. Khởi tạo dãy $B$ có $N$ phần tử, với $B(0) = -\infty$, và các phần tử khác bằng $+\infty$.
2. Duyệt $i$ từ 1 đến $N$, mỗi lần duyệt ta tìm vị trí $k$ đầu tiên có $B(k) \geq A(i)$ (sử dụng tìm kiếm nhị phân), gán $B(k) = A(i)$ (và $dp(i) = k$ nếu cần).
3. Độ dài dãy con tăng dài nhất là vị trí $k$ cuối cùng mà $B(k) \neq +\infty$, hoặc cũng có thể lấy giá trị lớn nhất trong quá trình duyệt.

~~dài dòng là vậy chứ thằng viết dùng set~~

**có thể làm bằng fenwick tree hay segtree bằng công thức truy hồi khác**
#### độ phức tạp: $O(n.log(n))$
## ZERON
### hướng:

1. **Khởi Tạo:**
   - Bạn cần kiểm tra tất cả các cách chèn các dấu `+`, `-`, và `!` vào giữa các số từ 1 đến $N$. Tổng số cách có thể là $3^{N-1}$.

2. **Tạo Các Kết Hợp Dấu:**
   - Sử dụng phương pháp quay lui (backtracking) để thử tất cả các kết hợp của dấu:
     - Bắt đầu từ vị trí đầu tiên và thử tất cả các dấu cho từng vị trí giữa các số.
     - Đệ quy đến vị trí tiếp theo cho đến khi tất cả các vị trí đều đã được gán dấu.

3. **Xây Dựng Biểu Thức:**
   - Sau khi chèn tất cả các dấu, xây dựng biểu thức từ mảng dấu đã tạo.
   - Biểu thức sẽ được tạo thành bằng cách nối các số với các dấu đã chèn.

4. **Tính Giá Trị Biểu Thức:**
   - Xử lý các dấu:
     - Dấu `!` được sử dụng để kết hợp các số thành một số lớn hơn.
     - Dấu `+` và `-` được sử dụng để thực hiện phép cộng và trừ.
   - Tính tổng của biểu thức và kiểm tra nếu tổng bằng 0.
   
~~code của thằng viết~~
```
ll res = 0, tmp = 1, p = 1;
for (int i = 2; i <= n; ++i) 
    {
    if (v[i] == '!') 
    {
        tmp = tmp * 10 + i;
    } else 
    {
        res += p * tmp;
        tmp = i; 
        if (v[i] == '+') p = 1;
        else p = -1;
    }
}
res += p * tmp;
```
5. **Lưu Trữ Kết Quả:**
   - Sử dụng một tập hợp để lưu các biểu thức đã tính để đảm bảo tính độc nhất.
   - Nếu một biểu thức đã tồn tại trong tập hợp, không cần tính toán lại.

6. **Đếm và In Kết Quả:**
   - Đếm số lượng các biểu thức thỏa mãn điều kiện tổng bằng 0.
   - In tất cả các biểu thức đã tìm được.


~~cảm ơn chatgpt đã giúp vài chữ đơn giản của thằng viết thành 1 bài văn~~
### độ phức tạp: $O(3^{N-1})$