# Solution Sơ loại OLP MTTN 2023 (Chuyên Tin) ## B1: TEAMBUILDING Tóm tắt: Đếm số cách chia mảng $a$ thành các nhóm liên tiếp. Mỗi nhóm thỏa mãn abcxyz. --- ### Sub 1: $n \le 20$ Có $n$ người thì sẽ có $n-1$ "khoảng trống" để ta đặt/ không đặt "vách ngăn" vào. Mỗi cách đặt như vậy là một cách chia nhóm. Ví dụ: [Hình vẽ] (soon^TM) Quay lui $O(2^n)$ --- ### Sub 2: $n \le 500$ Cài chưa chuẩn ? ### Sub 3: $n \le 5000$ Đặt $f(i)$ là số cách để chia $i$ người đầu. Từ $f(i)$ ta cập nhật $f(j)$ với $j > i$. Thêm một nhóm $i+1, i+2, i+3, ..., j$. Nếu được thì $\texttt{f(j) += f(i)}$ ![](https://i.imgur.com/CuJcjTQ.png) (Nguyễn Minh Huy, A5 K37 LQĐ) --- ## B2: FRUITMARKET Tóm tắt: Hoa sẽ đi mua quả theo vòng tròn: mỗi tiệm mua $\le 1$ quả xong rồi qua tiệm tiếp theo. Dừng lại khi không đủ tiền mua quả rẻ nhất. **Bước 1:** Tính xem thử Hoa đã đi được bao nhiêu vòng trước khi $m<$ tổng? Đặt $S$ là tổng dãy $a$. Số vòng $= [m / S]$. Giả sử sau khi mua các vòng thì còn dư $m'$ tiền. ### Thuật trâu: Mô phỏng lại toàn bộ quá trình mua hàng của Hoa. ```cpp for (int i = 1; m >= Min; i++) { if (i > n) i = 1; if (m >= a[i]) { m -= a[i]; ans++; } } ``` ### Sub 1: $n = 2$ Có các giai đoạn: - Còn đủ tiền mua $2$ quả - Chỉ đủ tiền mua quả rẻ nhất - Thiếu tiền Dùng phép chia để xem mỗi giai đoạn xảy ra trong bao lâu (bao nhiêu vòng?) ### Sub 2: $a_1 \le a_2 \le ... \le a_n$ Tính mảng tiền tố (prefix-sum). Mô phỏng lại các vòng đi chợ của Hoa. Do $a$ tăng dần nên những loại quả được mua sẽ là những quả ở các quầy đầu tiên. Do đó chỉ cần TKNP để xác định là mua tới quả nào thì hết tiền, xong lại bắt đầu vòng mới. ```cpp // Credit: OLP4CT587 void sub2() { int res = 0; //prefix-sum for (int i = 1; i <= n; i++) a[i] += a[i - 1]; //trở thành tổng i số đầu tiên while (m >= a[1]) { int k = upper_bound(a + 1, a + 1 + n, m) - a - 1; //upper_bound: tìm vị trí đầu tiên > hẳn m // lại -1 đi để ra vị trí cuối cùng <= res += (m / a[k]) * k; m = m % a[k]; } cout << res; } ``` ### Sub 3: $m \le \min a\cdot 10^6$ Đáp án chắc chắn $\le 10^6$. Tuy nhiên nếu cày trâu như trên thì sẽ TLE (theo lí thuyết) vì có trường hợp quả rẻ nhất nằm ở một vị trí duy nhất, và ta phải chạy for hết $n$ phần tử mới tìm được quầy rẻ đó để mua. VD: ``` n = 1e5, m = 1e18 a[i] = 10^9 với mọi i, ngoại trừ tại vị trí i = ? thì a[i] = 1 ``` Ý tưởng: http://online.vku.udn.vn/src/88371 Dùng DSU, tính mảng $nxt(i)$ là vị trí tiếp theo trên vòng tròn mà $\le m$ (số tiền còn hiện tại) ![](https://i.imgur.com/oyWvzsG.png) ### Sub 4: ### Đơn giản Thuật toán sau mô phỏng lại thuật trâu: ![](https://i.imgur.com/fP6HJ1x.png) Code trên của: baoduong23042007 Giải thích: Vòng lặp `while` bên ngoài thực hiện rất ít lần, vì câu lệnh `m %= sum` sẽ giảm $m$ đi ít nhất $2$ lần (nếu `m > sum`). Nếu `m < sum` thì phần "cày trâu" sẽ giảm $m$ đi xuống rất bé ? ### Phức tạp (Chưa kiểm chứng được đúng sai nhưng thấy có một số thí sinh code BIT) Xét $O(n)$ thời điểm đặc biệt: ngay khi $m$ giảm xuống bé hơn giá trị lớn thứ $i$ trong dãy $a$. Quầy hàng nào đắt hơn $m$ thì ta "xóa" nó đi. Về ý tưởng thì vẫn dùng phép chia (đi thành nhiều vòng) giống subtask 1, cho tới khi $m$ bé hơn mốc tiếp theo. Vậy việc cần quan tâm là xét trong một đoạn liên tiếp sẽ có bao nhiêu số chưa bị "xóa" Có cần BIT hay không? ## B3: DISTANCE Tóm tắt: Cho cây $n$ đỉnh. Có $k$ đỉnh đặc biệt. Hỏi khoảng cách từ $u$ tới đỉnh đặc biệt gần nhất ($\neq u$) là bao nhiêu? ### Sub 1: $n \le 200$ Thuật $O(n^2):$ Tính trước k/cách giữa mọi cặp đỉnh trên cây (BFS/DFS), lưu vào mảng. Sau đó chạy `for` $n^2$ để tìm cà phê gần nhất. ### Sub 2: $n \le 10^5, k \le 50$ Với mỗi $u$, ta duyệt qua $k$ đỉnh đặc biệt, cần lấy k/cách từ $u$ tới đỉnh đó. Vậy lúc đầu cần tính trước bằng cách BFS/DFS từ mỗi một trong $k$ đỉnh. Hoặc: dùng LCA để tính (phức tạp). ĐPT: $O(nk)$ ### Sub 3 + 4: Làm chưa tối ưu la http://online.vku.udn.vn/src/89811 QHĐ trên cây. DFS 2 lần để tính hai loại khoảng cách: - từ nút con cháu cập nhật lên $u$ - từ nút tổ tiên (nằm ngoài cây gốc $u$) cập nhật cho $u$. Đặt `dp_down[u], dp_up[u]` lần lượt là k/c loại 1,2 ở trên. Khoảng cách loại 1 tính được bằng dfs thông thường. Gọi dfs để tính `dp_down` của những nút con cháu trước, sau đó gán `dp_down[u] = max(dp_down[v] + w)`. https://viblo.asia/p/quy-hoach-dong-tren-cay-LzD5d9B4KjY Khoảng cách loại 2 phải tính ngược lại một chút. Tận dụng khoảng cách loại 1, và kết quả từ các nút cha & tổ tiên ta cập nhật cho các nút con cháu. Có hai trường hợp: - `dp_up[v] = dp_up[u] + w` (từ nút tổ tiên của $u$ cập nhật cho $v$) - Từ một nhánh khác trong cây con gốc $u$, gọi là $v_2$, cập nhật cho $v$. Ta có `dp_up[v] = dp_down[v2] + w2 + w1` (từ nút $v_2$, đi theo hai cạnh để tới $u$, rồi tới $v$) Nếu nhóm biểu thức trên lại, ta thấy cần lấy $\max$ của tất cả `dp_down[.] + w[.]` của những nhánh khác $v$. #### Sub 3: Ta lưu hết vào một `multiset`, khi duyệt tới nhánh hiện tại thì xóa nó đi để tính, xong lại thêm vào. ĐPT $O(n\log)$ #### Sub 4: Cần tìm $\max$ mọi nhánh còn lại nhanh hơn. Có thể: - Hướng 1: Làm giống prefix-sum - Hướng 2: Lưu lại hai giá trị bé nhất. ## B4: CARDGAME ### $O(n^2)$ Thử hết mọi đoạn con (chạy for $l,r$), lấy tổng đoạn trừ max ### Full (idea) Giả sử $a_i$ lớn hơn mọi số trong đoạn $[l,r]$ (dùng stack/ gì đó để tìm l min, r max). Tính prefix-sum thôi: Lấy max của S từ i tới r, trừ cho min của S từ l-1 tới i-1. Segment Tree
{"metaMigratedAt":"2023-06-17T22:46:53.742Z","metaMigratedFrom":"Content","title":"Solution Sơ loại OLP MTTN 2023 (Chuyên Tin)","breaks":true,"contributors":"[{\"id\":\"03d84148-3077-4010-974a-4daa587c03be\",\"add\":5936,\"del\":726},{\"id\":\"8b25894d-ff5a-4d30-b50c-4b7f44c11fef\",\"add\":39,\"del\":0},{\"id\":null,\"add\":274,\"del\":0}]"}
    1552 views
   owned this note