## Bài 1. Dãy con có độ dài bị chặn ### Subtask 1 Duyệt $\mathcal{O}(Q)$ truy vấn, với mỗi truy vấn: - Duyệt $\mathcal{O}(N^2)$ đoạn con, tính tổng trong $\mathcal{O}(N)$ và cập nhật đáp án. **Độ phức tạp:** $\mathcal{O}(QN^3)$ ### Subtask 2 Duyệt $\mathcal{O}(Q)$ truy vấn, với mỗi truy vấn: - Duyệt $\mathcal{O}(N^2)$ đoạn con, tính tổng trong $\mathcal{O}(1)$ và cập nhật đáp án. **Độ phức tạp:** $\mathcal{O}(QN^2)$ ### Subtask 3 Duyệt $N$ độ dài $1\to N$, với mỗi độ dài $\ell$, duyệt $\mathcal{O}(N)$ và lưu lại tổng lớn nhất của một đoạn con độ dài $\ell$ trong dãy vào mảng `best[l]`. Duyệt $Q$ truy vấn, với truy vấn thứ $i$, ta đi tìm $\max_{L_i \le x \le H_i}\{best[x]\}$ trong $\mathcal{O}(N)$. Độ phức tạp $\mathcal{O}(N^2 + QN)$ ### Subtask 4 Xét mảng tổng tiền tố $S$, với $S[i] = \sum\limits_{x = 1}^{i} a[x]$. Ta có thể dựng mảng trong $O(N)$ bằng công thức truy hồi sau: $$ S[i] = S[i-1] + a[i] $$ Xét $Q$ truy vấn, với mỗi truy vấn $(L_{i}, H_{i})$: - **Nhận xét:** Ta tiến hành duyệt đầu phải $y$ của đoạn con trong $O(N)$, với mỗi $y (1 \le y \le N)$. Ta cần tìm $x$ sao cho: $$ \begin{cases} 0 \le x < y \\ L_{i} \le y-x \le H_i \\ \max\{S[y]-S[x]\} \end{cases} $$ Do $y$ và $S[y]$ cố định, điều kiện trở thành: $$ \begin{cases} 0 \le x < y \\ y-L_{i} \ge x \ge y-H_{i} \\ \min\{S[x]\} \end{cases} $$ Từ đây, thuật toán là với mỗi truy vấn, ta duyệt $y$ trong $O(N)$, sau đó tìm $\min\{S[x]\}$ với $x \in [y-H_{i}, y-L_{i}]$ trong $\mathcal{O}(1)$ bằng [[sparse_table|Sparse Table]]. **Độ phức tạp:** $\mathcal{O}(QN)$ ## Bài 2. Mạng thông suốt ### Subtask 1 Duyệt $\mathcal{O}(n^2)$ cặp đỉnh $(u, v)$ trong đồ thị, nếu chưa tồn tại cung $u \to v$, thì thêm cung vào và kiểm tra tính liên thông mạnh của đồ thị này trong $O(n \cdot m)$ bằng cách xét từng đỉnh rồi [[dfs_bfs|DFS]] xem đỉnh có thể thăm được toàn bộ các đỉnh còn lại không. **Độ phức tạp:** $\mathcal{O}(n^{3}\cdot m)$ ### Subtask 2 **Nhận xét:** Đáp án của bài toán là `YES` khi và chỉ khi: - Đồ thị liên thông mạnh. Trong trường hợp này ta có thể thêm cạnh bất kì. - Đồ thị gồm nhiều thành phần liên thông mạnh nối liên tiếp nhau theo dạng: $C_1 \to C_2 \to C_3 \to \dots \to C_k$. Trong trường hợp này, ta thêm một cung $u\to v$ với $u$ là một đỉnh bất kì thuộc $C_k$ và $v$ là một đỉnh bất kì thuộc $C_1$ Để xác định các thành phần liên thông, ta có thể dùng $n$ lần [[dfs_bfs|DFS]] từ mỗi đỉnh, đánh dấu $c[i][j] = 1$ nếu tồn tại đường đi từ $i$ đến $j$. Nếu $c[i][j] = c[j][i] = 1$ cho một cặp $(i, j)$ bất kì, ta sẽ dùng [[DSU]] để unite hai đỉnh này lại. Độ phức tạp: $\mathcal{O}(n \cdot m)$ ### Subtask 3 Ta dùng thuật toán [[strongly_connected_component|Tarjan]] để xác định các thành phần liên thông trong $\mathcal{O}(m)$. ## Bài 3. Đếm dãy ### Subtask 1 Duyệt $O(2^n)$ cách chọn, với mỗi cách chọn kiểm tra điều kiện đề bài trong hoặc $O(n)$ **Độ phức tạp:** $O(2^{n}\cdot n)$ #### Đệ quy ```c++ void try(int i, int open, int close) { if (i == n) { if (open == close) ++ans; return; } try(i+1, open, close); if (s[i] == '(' && close == 0) try(i+1, open+1, close); if (s[i] == ')') try(i+1, open, close+1); } ``` #### Khử đệ quy ```c++ int ans = 0; for(int mask = 0; mask < (1ll<<n); ++mask) { int open = 0, close = 0; bool ok = true; for(int i = 0; i < n; ++i) if ((mask>>i)&1) { if (s[i] == '(') { if (close > 0) ok = false; open++; } else close++; } if (open != close) ok = false; if (ok) ++ans; } cout << ans; ``` ### Subtask 2 Gọi $\text{dp}[L][R][open][close]$ là số cách chọn dãy ngoặc trong đoạn $[L, R]$, đã chọn được $open$ ngoặc mở và $close$ ngoặc đóng. $$ \begin{align*}\\ &\text{dp}[i][i][1][0] = 1 \text{ nếu } s[i] = '('\\ &\text{dp}[i][i][0][1] = 1 \text{ nếu } s[i] = ')' \\ &\text{dp}[L-1][R][open][close] += \text{dp}[L][R][open][close] \\ &\text{dp}[L-1][R][open+1][close] += \text{dp}[L][R][open][close] \text{ nếu } S[L-1] = '(' \\ &\text{dp}[L][R+1][open][close] += \text{dp}[L][R][open][close] \\ &\text{dp}[L-1][R][open][close+1] += \text{dp}[L][R][open][close] \text{ nếu } S[R+1] = ')' \\ \end{align*} $$ **Độ phức tạp:** $\mathcal{O}(n^4)$ ### Subtask 3 Gọi $\text{dp}[i][open][close]$ là số cách chọn dãy ngoặc khi xét đến $i$, có $open$ ngoặc mở và $close$ ngoặc đóng. Công thức quy hoạch động: $$ \begin{align*} &\text{dp}[0][0][0] = 1\\ &\text{dp}[i+1][open][close] += \text{dp}[i][open][close]\\ &\text{Nếu } s[i] = '(', \text{dp}[i+1][open+1][0] += dp[i][open][0]\\ &\text{Nếu } s[i] = ')', \text{dp}[i+1][open][close+1] += dp[i][open][close] \end{align*} $$ Đáp án là $$ \sum\limits_{i=1}^{n} \text{dp}[n][i][i] $$ **Độ phức tạp:** $\mathcal{O}(n^3)$ ### Subtask 4 Tính $L[i][open]$ là số cách chọn $open$ ngoặc mở nằm bên trái $i$. $R[i][close]$ là số cách chọn $close$ ngoặc đóng nằm bên phải $i$. Duyệt $i$ từ $0$ đến $n-1$, chọn $i$ là điểm trung gian và $i$ là **ngoặc mở**, duyệt $k$ là số lượng ngoặc mở và số lượng ngoặc đóng, đếm số cách chọn $k$ ngoặc mở ở bên trái $i$ và $k$ ngoặc đóng ở bên phải $i$. Số lượng dãy ngoặc đẹp trong trường hợp này sẽ là $L[i][k] \times R[i][k]$ ```c++ const int N = 9005; const int MOD = 1e9+19972207; int L[N][N], R[N][N]; void add(int &a, int b) { a += b; if (a >= MOD) a-= MOD; } ... memset(L, 0, sizeof L); memset(R, 0, sizeof R); for(int i = 0; i < n; ++i) for(int open = 0; open <= n; ++open) if (dp[i][open]) { add(dp[i+1][open], dp[i][open]); if (s[i] == '(') add(dp[i+1][open+1], dp[i][open]); } // Tinh R ... int ans = 0; for(int i = 0; i < n; ++i) for(int cnt = 1; cnt <= n; ++cnt) if (s[i] == '(') add(ans, 1ll*L[i][cnt]*R[i][cnt]%MOD); cout << ans; ``` **Độ phức tạp:** $\mathcal{O}(n^2)$ ### Subtask 5 Duyệt $i$ từ $0$ đến $n-1$, chọn $i$ là điểm trung gian và $i$ là **ngoặc mở**, duyệt $k$ là số lượng ngoặc mở và số lượng ngoặc đóng, đếm số cách chọn $k$ ngoặc mở ở bên trái $i$ và $k$ ngoặc đóng ở bên phải $i$. Số lượng dãy ngoặc đẹp trong trường hợp này sẽ là $C_{x}^{k} \times C_{y}^{k}$ với $x$ là số ngoặc mở bên trái $i$, $y$ là số ngoặc đóng bên phải $i$. Như vậy, với mỗi vị trí $i \in [0, n-1]$, ta có tổng số ngoặc đẹp là $$ \sum\limits_{k = 1}^{\min\{x, y\}}C_{x}^{k} \times C_{y}^{k} = \sum\limits_{k = 1}^{\min\{x, y\}}C_{x}^{x-k} \times C_{y}^{k} = C_{x+y}^{x} - 1 $$ Ta duy trì $x$ và $y$, trong quá trình duyệt $i$. Sau đó tính $C_{x+y}^n$ trong $O(1)$ hoặc $O(\log n)$. **Độ phức tạp:** $\mathcal{O}(n \log n)$ hoặc $O(n)$ ==0==1==0==11==0==0==1==0==111== i k = 2 00==0== 1==1==1==1==