VOI2024 Chuyên Bảo Lộc Hướng dẫn giải bài tập 11-12/12/2023 Ngày 14 tháng 12 năm 2023 Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Bài 1. Lập danh sách – MAKELIST Bài 2. Quy hoạch đường phố – ZONING Bài 3. Lẩu băng chuyền – HOTPOT Bài 1. Độ đơn giản – SIMPLE Bài 2. Nối đường – DSUR Bài 3. Tàu điện ngầm Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Xếp hình – PUZZLE I Bộ xếp hình gồm N miếng thuộc một trong 8 loại sau: I Bộ xếp hình sẽ có đúng một mảnh thuộc loại 5 hoặc 6 (để xếp bên trái cùng) và đúng một mảnh thuộc loại 7 hoặc 8 (để xếp bên phải cùng). Tất cả các miếng hình phải giữ nguyên trạng thái, không được xoay, lật, cắt,... I Hai hình có thể gọi là ghép với nhau nếu như nó có thể khớp với nhau theo hướng được ghép. I Luật chơi: "Trên mỗi mảnh ghép có ghi một con số khác nhau. Nhiệm vụ là phải ghép tất cả N mảnh ghép thành một hàng ngang. Nếu có nhiều cách ghép thì phải chọn cách ghép sao cho chuỗi các số ghi trên các mảnh ghép đọc từ trái qua phải có thứ tự từ điển nhỏ nhất." I Yêu cầu: Hãy sắp xếp các mảnh ghép theo yêu cầu trên. Subtask 1 (N ≤ 4) Subtask đầu tiên sẽ được giải quyết bằng cách thực hiện một số thao tác if một cách hợp lý. Subtask 2 (N ≤ 10): O(N!) Duyệt hoán vị của các mảnh ghép. Subtask 3 (Không có các mảnh ghép thuộc loại 2 và 3) Chỉ cần quan sát và lựa chọn giữa một trong hai loại mảnh ghép 1 và 4 để chèn vào vị trí tiếp theo, và ta sẽ luôn ưu tiên những mảnh ghép có số ghi trên nhỏ nhất trước. Nếu như sau quá trình ghép thừa ra một số mảnh ghép không thể lắp, trò chơi sẽ không thể hoàn thành. Subtask 4 (Có nhiều nhất một mảnh ghép thuộc loại 1 hoặc 4) I Giả sử ta đã ghép được một số mảnh ghép và mảnh ghép cuối cùng chứa hình lõm nằm bên phải. Vậy mảnh ghép tiếp theo được ghép vào phải có một hình lồi nằm tay trái, và điều đó chỉ có thể thực hiện bởi mảnh ghép loại 1 hoặc 2. I Nếu ta lựa chọn mảnh ghép loại 2 để lắp vào, thì mảnh ghép tiếp theo ta phải lắp vẫn sẽ phải thuộc một trong hai loại 1 hoặc 2. I Mặt khác, nếu ta lựa chọn mảnh ghép loại 1 để lắp vào, bước tiếp theo chúng ta cần làm chính là đặt một mảnh ghép thuộc một trong hai loại 3 hoặc 4. Do đó, ta có thể coi mảnh loại 1 như là một mảnh giúp thay đổi hình thức của mảnh tiếp theo cần đặt. Tương tự đối với mảnh 4. I Vì vậy, ta trước tiên sẽ đặt tất cả mảnh ghép loại 2 (hoặc 3 tùy thuộc vào mảnh ghép đầu tiên là 5 hay 6), sau đó đặt một mảnh loại 1(hoặc 4), và cuối cùng đặt tất cả các mảnh ghép loại 3 (hoặc 2) còn lại. Ta luôn ưu tiên những mảnh ghép có số ghi trên nhỏ hơn trước trong quá trình đặt những mảnh ghép cùng loại. I Lưu ý: cẩn thận với trường hợp không có mảnh ghép loại 1 hoặc 4. Subtask 5 (N ≤ 105 ): O(N) I Áp dụng thuật toán tham lam bằng cách luôn luôn lựa chọn mảnh ghép có thể đặt vào tiếp theo và có số điền trên là nhỏ nhất. I Nhưng vấn đề ở đây chính là có thể thừa ra một số mảnh ghép loại 2 hoặc 3 không được đặt vào (nếu như có một mảnh ghép loại khác một trong hai loại trên bị dư ra, trò chơi sẽ không thể được hoàn thành). I Khắc phục điều này bằng cách chèn những mảnh ghép đó vào vị trí cuối cùng (xa nhất từ trái sang) nơi mà chúng có thể vừa. Vị trí được đặt đó sẽ luôn tiếp giáp với mảnh cuối cùng của loại 1 hoặc 4. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Sơn nhà ngày Tết – PAINTHOUSE I Khu biệt thự gồm N căn, có 109 màu. Căn biệt thự thứ i có Ai thùng sơn trong nhà có các màu mang số hiệu 1, 2, ..., Ai . Các chủ căn biệt thự không muốn nhà mình có cùng màu sơn với hai căn bên cạnh. I Một kế hoạch sơn lại các căn biệt thự được xem là thỏa mãn nếu như căn biệt thự thứ i được sơn lại màu mang số hiệu Ci ∈ [1, Ai ] và Ci 6= Ci+1 , ∀i ∈ [1, N − 1]. I Yêu cầu: Hãy đếm xem có bao nhiêu kế hoạch có thể thực hiện sơn lại tất cả các căn biệt thự thỏa mãn yêu cầu trên. Do kết quả có thể rất lớn nên bạn chỉ cần tìm kết quả sau khi chia lấy dư cho 998244353. Subtask 1 (20%): N, Ai ≤ 8: O(ANi ) Duyệt hết tất cả các trường hợp bằng đệ quy sau đó kiểm tra kết quả thỏa mãn với bài toán hay không rồi cộng vào ans. Subtask 2 (20%): N ≤ 20: O(2N ) I Giả sử ta tạo ra một dãy Ci bất kì mà Ci ∈ [1, Ai ]. Sau đó ta xếp các giá trị bằng nhau liên tiếp thành một block. I Duyệt nhị phân trên N vị trí, với các vị trí liên tiếp nhau có cùng giá trị ta sẽ xem nó thuộc cùng một block. I Ví dụ với N = 10 và dãy nhị phân 0001101110 thì sẽ tương đương với việc các vị trí từ 1 đến 10 được chia thành các block [1, 2, 3]; [4, 5]; [6]; [7, 8, 9]; [10]. Với các giá trị trong một block, ta sẽ tạo ra các giá trị Ci trong block đó bằng nhau. Hay nói cách khác, số cách chọn các giá trị Ci trong block chính là giá trị nhỏ nhất trong block đó. Đối với ví dụ trên thì số lượng dãy tạo ra được chính là min(A1 , A2 , A3 ) ∗ min(A4 , A5 ) ∗ A6 ∗ min(A7 , A8 , A9 ) ∗ A10 . Như vậy, với dãy bit trên ta có thể tạo ra được một dãy Ci có thể chia thành nhiều nhất 5 block có các giá trị Ci bằng nhau. Subtask 2 (20%): N ≤ 20: O(2N ) I Vậy mục đích của bài toán là đếm số lượng cách chọn Ci để tạo ra được đúng N block. I Gọi f (k)= số lượng dãy Ci được tạo ra mà có thể chia thành nhiều nhất k block. I Như vậy với ví dụ trên thì kết quả được cộng vào f(5). I Theo bao hàm loại trừ thì kết quả bài toán là: f (N) − f (N − 1) + f (N − 2) − ... Subtask 3 (20%): N ≤ 2000 : O(N 2 ) Dựa vào ý tưởng Subtask 2 ta thực hiện QHĐ như sau. I Gọi dp[i, j] = số lượng cách tạo ra dãy C từ dãy A1 , A2 , ..., Ai mà dãy C được chia thành nhiều nhất j block: X dp[i, j] = dp[i 0 , j − 1] ∗ min(Ai 0 +1 , Ai 0 +2 , ..., Ai ). i 0 <i I Kết quả là: dp[N, N] − dp[N, N − 1] + dp[N, N − 2] − ... I ĐPT lúc này đang là O(N 3 ). Subtask 3 (20%): N ≤ 2000 : O(N 2 ) I Nhận xét: khi thực hiện dp[i, j] ta sẽ chỉ quan tâm j lẻ hoặc j chẵn. Bởi vì đến sau cùng khi j cùng tính chẵn lẻ với N thì ta cộng vào kết quả, ngược lại ta thực hiện trừ vào kết quả. I Vậy nếu gọi dp[i, t] với t ∈ [0, 1] thay vì dp[i, j] với j ∈ [1, i] thì ta xây dựng lại công thức như sau: X dp[i 0 , 1 − t] ∗ min(Ai 0 +1 , ..., Ai ). dp[i, t] = i 0 <i I Kết quả lúc này là: dp[N, N mod 2] − dp[N, 1 − (N mod 2)]. Subtask 4 (20%): |Ai − Ai+1 | ≤ 10, ∀i ∈ [1, N − 1] : O(40 ∗ N) Gọi dp[i, x] = số lượng dãy C thỏa mãn được tạo từ dãy A1 , A2 , ..., Ai và Ci = Ai + x. Với |Ai − Ai+1 | ≤ 10 ta nhận thấy ta chỉ cần quan tâm x ∈ [−20, 20]. Subtask 5 (10%): N ≤ 200000 : O(N) Dựa trên ý tưởng của subtask 3, ta gọi L[i] là vị trí lớn nhất nhỏ hơn i và thỏa mãn A[L[i]] < A[i]. Ta có thể dễ dàng thực hiện tính L[i] trong O(N log N) hay thậm chí O(N). Nhận thấy: I Với j ∈ [L[i] + 1, i] thì Aj ≤ Ai : dp[i, t] = X dp[j, 1 − t] ∗ Ai . L[i]<j<i I Với j ≤ L[i]: dp[i, t] = dp[L[i], t]. Vậy: dp[i, t] = dp[L[i], t] + (sum[i − 1, 1 − t] − sum[L[i] − 1, 1 − t]) ∗ Ai , với sum[i, t] = dp[1, t] + dp[2, t] + ... + dp[i, t]. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Du lịch – TRAVEL I Có N danh lam thắng cảnh có dạng hình cây, N − 1 tuyến tàu nhanh nối các điểm. I Khi di chuyển trên một tuyến tàu từ điểm này đến điểm khác, xem như là đã đến chơi hai vị trí đó. I Tuyến tàu di chuyển giữa hai điểm ai và bi mang lại niềm vui được đo theo thang ci /W . I Nếu du khách có một lịch trình di chuyển trên nhiều tuyến đường thì niềm vui của du khách đó được tính là tổng của các giá trị niềm vui trên từng tuyến đường mang lại. I Yêu cầu: Hãy tính niềm vui lớn nhất đạt được ở mỗi hành trình mà không đi một điểm quá 1 lần. I Có Q truy vấn, mỗi truy vấn thay đổi giá trị niềm vui trên đúng một cạnh, lưu ý truy vấn i có thể ảnh hưởng đến các truy vấn sau, các truy vấn diễn ra tuần tự. Subtask 1 (N, Q ≤ 100; W ≤ 104 ): O(Q ∗ N 2 ) Chỉ cần cập nhập lại trọng số của các cạnh đúng với đề yêu cầu. Sau đó thực hiện DFS từ mỗi đỉnh để tìm khoảng cách lớn nhất từ đỉnh khác đến nó. Lấy giá trị lớn nhất làm kết quả. Subtask 2 (N, Q ≤ 5000; W ≤ 104 ): O(Q ∗ N) Ta cần tìm đường kính của cây bằng cách sử dụng QHĐ. Xem đỉnh 1 là gốc, gọi D[u] là khoảng cách lớn nhất từ đỉnh u đến đỉnh là lá nằm trong cây con gốc u: D[u] = max(D[u], D[v ] + wu,v ). Bây giờ ta thực hiện duyệt qua các đỉnh. Khi duyệt tới đỉnh u, với v1 , v2 (v1 6= v2 ) là đỉnh con liền kề của đỉnh u. Ta cập nhập kết quả: max(D[v1 ] + D[v2 ] + wu,v1 + wu,v2 ). Subtask 3 (W ≤ 104 và các tuyến đường có dạng {1, i}): O((Q + N) ∗ log N) Đồ thị lúc này là đồ thị hình sao, có nghĩa là đường kính chỉ bao gồm hai cạnh và cả hai cạnh đó đều nối với đỉnh 1. Do đó ta chỉ cần duy trì và thay đổi trọng số của các cạnh trong multiset và kết quả sẽ là tổng của 2 giá trị lớn nhất trong multiset đó. Subtask 4 (W ≤ 104 và các tuyến đường có dạng {i, 2i} và {i, 2i + 1}): O(N + Q ∗ log N) I Vẫn sử dụng QHĐ giống subtask 2. Thêm vào đó ta gọi S[u] là đường kính của cây con gốc u. S[u] = max(D[u], S[v ], max(D[v1 ] + D[v2 ] + wu,v1 + wu,v2 )), với v , v1 , v2 là đỉnh con liền kề với đỉnh u. I Hiển nhiên kết quả sẽ là S[1]. I Khi thay đổi trọng số của cạnh (u, v ) thì ta chỉ cập nhập lại giá trị của S[x], D[x] với x là tổ tiên của u, v . Vì đồ thị là dạng cây nhị phân nên việc cập nhập S[x], D[x] chỉ mất trong thời gian log N. Subtask 5 (Dữ liệu đảm bảo rằng luôn tồn tại một hành trình mang lại niềm vui lớn nhất cho bạn và đi qua khu danh lam thắng cảnh 1): O(N + Q ∗ log N) I Vì đường kính luôn đi qua đỉnh 1 nên kết quả sẽ là max(D[v1 ] + D[v2 ] + w1,v1 + w1,v2 ), với v1 , v2 là đỉnh con liền kề của đỉnh 1. I Ta thực hiện trải cây theo thứ tự DFS. Sử dụng Segment Tree Lazy để duy trì và cập nhập khoảng cách từ các đỉnh đến gốc (đỉnh 1). Từ đó tính các giá trị D[vi ]. I Để tìm kết quả ta cũng sử dụng multiset như subtask 3, duy trì và thay đổi giá trị của D[vi ] + w1,vi với vi là đỉnh con liền kề của đỉnh 1. Sau đó lấy tổng của 2 giá trị lớn nhất. Subtask 6 (2 ≤ N ≤ 105 ; 1 ≤ Q ≤ 105 ; 1 ≤ W ≤ 2 ∗ 1013 ): O(N log N + Q log N) I Thực hiện “nén cây Euler Tour” bằng DFS để biến cây thành một dãy số, sau đó phân tích xem đường kính của cây được biểu diễn như thế nào trên dãy số này. Ta sẽ biểu diễn một cây theo thành một Euler Tour như ví dụ dưới đây: I Khi nén cây sẽ tạo thành dãy 1,2,3,2,4,2,5,2,1,6,7,6,8,9,8,6,1 chính là thứ tự thăm các đỉnh trên cây này khi DFS. Ta đánh số chuỗi Euler Tour này từ 1 đến m từ trái qua phải và gọi h[i] là độ sâu của đỉnh nằm trên vị trí thứ i trên Euler Tour. Subtask 6 (2 ≤ N ≤ 105 ; 1 ≤ Q ≤ 105 ; 1 ≤ W ≤ 2 ∗ 1013 ): O(N log N + Q log N) I Vậy nếu gọi p[u] là vị trí của đỉnh u trên Euler Tour (nhiều vị trí thì chọn vị trí trái nhất) và d[u] là độ sâu của đỉnh u trên đồ thị. Khi đó, ta xét khoảng cách của hai đỉnh u và v là d[u]+d[v ]−2∗d[LCA(u, v )] = h[p[u]]+h[p[v ]]−2∗h[p[LCA(u, v )]]. I Một trong những tính chất của chuỗi Euler Tour đó là từ vị trí p[u] đến p[v ] (giả sử p[u] < p[v ]) trên chuỗi này, sẽ có sự xuất hiện của LCA(u, v ), gọi vị trí đó là x khi đó ta có h[x] = mini=p[u]...p[v ] {h[i]}. Khoảng cách từ u đến v : h[p[u]] + h[p[v ]] − 2 ∗ mini=p[u]...p[v ] {h[i]} I Quay trở lại bài toán, vậy kết quả bài toán là: max {h[p[u]] + h[p[v ]] − 2 ∗ 1≤u,v ≤n = min {h[i]}} i=p[u]...p[v ] max {h[p[u]] − 2 ∗ h[x] + h[p[v ]]} p[u]≤x≤p[v ] = max 1≤i≤j≤k≤m {h[i] − 2 ∗ h[j] + h[k]}. Ta có kết quả: max1≤i≤j≤k≤m {h[i] − 2 ∗ h[j] + h[k]} trên Euler Tour. Subtask 6 (2 ≤ N ≤ 105 ; 1 ≤ Q ≤ 105 ; 1 ≤ W ≤ 2 ∗ 1013 ): O(N log N + Q log N) I Để xử lý bài toán này kèm theo update các truy vấn, ta sẽ sử dụng segment tree quản lý các trường sau: I s[0, 0] = h[i] I s[0, 1] = h[i] − 2 ∗ h[j] I s[0, 2] = h[i] − 2 ∗ h[j] + h[k] I s[1, 1] = −2 ∗ h[j] I s[1, 2] = −2 ∗ h[j] + h[k] I s[2, 2] = h[k]. I Khi thay đổi giá trị một cạnh, bản chất ta chỉ cần thay đổi giá trị h[i] của một đoạn con liên tiếp biểu diễn cho sự thay đổi độ sâu của một cây con thuộc gốc u nào đó trên cây. Như vậy ở đây ta sẽ sử dụng segment tree lazy propagation. I Khi merge hai đoạn con L và R lại, ta chỉ cần thực hiện công thức: s[i, j] = max {L.s[i, j], R.s[i, j], L.s[i, k] + R.s[k + 1, j]}. k=i...j−1 I Mỗi truy vấn chỉ mất O(log(N)). Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Cá mập cắn cáp – SHARK I Có N đảo quốc và hệ thống dây cáp quang nối 2 đảo kề nhau, hệ thống dây cáp này đã tạo thành một đa giác đều với mỗi đỉnh là vị trí một quốc gia. Người ta muốn xây dựng thêm các đường dây cáp quang mới dưới vùng biển được giới hạn bởi các dây cáp ban đầu. Các quốc gia sẽ cùng xây dựng thêm N − 3 tuyến cáp mới, đảm bảo không có hai tuyến nào giao nhau (trừ hai đầu mút). Tuyến cáp thứ k nối hai quốc gia phân biệt ik và jk . I Một cỗ máy cá mập có thể cắn đứt mọi dây cáp quang trên đường đi trên một đường thẳng của nó. Hãy tính toán xem số lượng dây cáp tối đa có thể bị cắn đứt là bao nhiêu. Subtask 1 (10%): ik = 1, ∀k Khi tất cả đường chéo được kẻ từ 1 đỉnh. Ta hoàn toàn có thể kẻ một đường thẳng đi qua tất cả các đường chéo, ngoài ra còn đi qua hai cạnh của đa giác. Vì vậy, kết quả sẽ là N − 1. Subtask 2,3 (20%): N ≤ 500 I Đưa đa giác lên hệ trục Oxy . Đặt gốc O là tâm, vẽ một đường tròn bán kính R. Chọn N điểm cách đều nhau trên đường tròn đó, N điểm đó sẽ là N đỉnh của đa giác. I Duyệt từng cặp cạnh của đa giác, vẽ một đoạn thẳng đi từ một điểm của cạnh này tới một điểm của cạnh kia. Đếm số cạnh giao với đường thẳng vừa kẻ. Subtask 4 (30%): N ≤ 5000 I Ý tưởng: xem từng tam giác được tạo là mỗi đỉnh, với hai tam giác có chung cạnh sẽ kề nhau. Bài toán sẽ trở thành tìm đường đi dài nhất trên cây. I Xét từng đỉnh của đa giác. Tại đỉnh thứ i, gọi Si là tập các đỉnh thuộc đa giác sao cho tồn tại một đường chéo kết nối với đỉnh i. Sắp xếp các đỉnh theo chiều kim đồng hồ. Với đỉnh S[j], ba đỉnh i, S[j], S[j + 1] tạo thành một tam giác. Ta sử dụng map đánh số cho tam giác này. Ngoài ra, tam giác (i, S[j], S[j + 1]) sẽ có một cạnh chung với tam giác (i, S[j + 1], S[j + 2]). I Sau khi dựng đồ thị (dạng cây), từ mỗi đỉnh, BFS hoặc DFS tìm đường đi dài nhất. Subtask 5 (20%): N ≤ 105 I Dựng đồ thị tương tự ở Subtask 4, nhưng tối ưu phần tìm đường đi dài nhất. I Coi đỉnh 1 là gốc. I Gọi f [i] là đường đi dài nhất từ đỉnh i trong cây con gốc i. Có: f [i] = max(f [j] + 1) với j là con của i. Ngoài ra, khi ta kết nối đường đi dài nhất trong cây con gốc i, lên i, và xuống đường đi dài nhì trong cây con gốc i, ta sẽ được đường đi dài nhất trong cây con gốc i. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Cho cừu vào chuồng – BARNSHEEP I Nông trại của Phương có N con cừu và N căn chuồng cừu. Phương thu được 2 dãy số nguyên s1 , s2 , ..., sN là kích thước từng con cừu và t1 , t2 , ..., tN là kích thước từng căn chuồng. Con cừu thứ i có thể ở vào căn chuồng thứ j nếu si ≤ tj và mỗi chuồng cũng chỉ chứa tối đa được một con cừu. I Một cách phân bố các con cừu vào chuồng được gọi là "cực đại" nếu như trong cách phân bố đó ta không thể thêm bất kỳ con cừu nào vào một chuồng khác được nữa. I Yêu cầu: Hãy giúp Phương xác định được có bao nhiêu cách phân bố "cực đại" khác nhau. Subtask 1 (N ≤ 8): O(N! ∗ N) Duyệt hoán vị tất cả các trường hợp phân bố cừu vào các chuồng. Subtask 2 (N ≤ 50): O(N 3 ) Ta sắp xếp kích thước các con cừu cùng với các chuồng theo thứ tự không giảm. Thực hiện duyệt con cừu Sx là con cừu có kích thước nhỏ nhất và không được phân bố vào bất kỳ chuồng nào. Ta có: I Tất cả những con cừu Sy với y < x bắt buộc phải có chuồng. I Tất cả những chuồng có Ty ≥ Sx phải có chứa cừu. Subtask 2 (N ≤ 50): O(N 3 ) Duyệt theo thứ tự kích thước không giảm của cả cừu và chuồng, gọi dp[i, c] là số cách phân bố để con cừu Sx là con cừu nhỏ nhất không có chuồng khi xét đến phần tử thứ i (có thể là cừu hoặc chuồng) và có c con cừu ta cho vào chuồng nhưng hiện chưa có chuồng, xem như chúng đang ở trong một hàng đợi. Khởi tạo dp[0, 0] = 1 I Khi phần tử thứ i là một con cừu: I Nếu i < x tức là con cừu bắt buộc phải có chuồng: dp[i, c]+ = dp[i − 1, c − 1]. I Nếu i ≥ x thì con cừu này có thể được phân vào chuồng hoặc không: dp[i, c]+ = dp[i − 1, c − 1] + dp[i, c]. I Khi phần tử thứ i là một cái chuồng: I Nếu Ti ≥ Sx thì chuồng bắt buộc chứa cừu: dp[i, c]+ = dp[i − 1, c + 1] ∗ (c + 1). I Nếu Ti < Sx thì chuồng không bắt buộc chứa cừu: dp[i, c]+ = dp[i − 1, c] + dp[i − 1, c + 1] ∗ (c + 1). Subtask 3 (N ≤ 3000): O(N 2 ) Ta không cần thực hiện việc duyệt con cừu Sx như subtask 2, thay vào đó ta có thêm 1 chiều trong QHĐ với hai giá trị 0/1 cho biết: I 0 là chưa chọn ra con cừu nhỏ nhất không có chuồng. I 1 là đã chọn ra con cừu nhỏ nhất không có chuồng. Duyệt theo thứ tự kích thước không giảm của cà cừu và chuồng. I Khi phần từ thứ i là cừu: I dp[i, c, l]+ = dp[i, c − 1, l]: con cừu này được phân vào chuồng (vào hàng đợi). I dp[i, c, 1]+ = dp[i, c, l]: con cừu này không được phân vào chuồng. I Khi phần tử thứ i là chuồng: I dp[i, c, l]+ = dp[i − 1, c + 1, l] ∗ (c + 1). I dp[i, c, 0]+ = dp[i − 1, c, 0]. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Rèm cửa — CURTAINS I Bình biểu diễn trên một sân khấu gồm n phần được đánh số từ 1 đến n từ trái sang phải. Anh ta còn có m tấm rèm được đánh số từ 1 đến m. I Mỗi tấm trong m tấm rèm này có thể được hạ xuống. Rèm i hạ xuống sẽ che phủ các phần sân khấu từ l[i] đến r [i]. Một cấu hình rèm là một tập các tấm rèm được hạ xuống. Cho một cấu hình rèm, phần sân khấu x (1 ≤ x ≤ n) được gọi là che phủ khi và chỉ khi tồn tại một tấm rèm i hạ xuống sao cho l[i] ≤ x ≤ r [i]. I Bình muốn đưa ra tổng số q màn trình diễn, được đánh số từ 1 đến q. Đối với mỗi màn trình diễn j, Bình yêu cầu cấu hình rèm sao cho các phần sân khấu từ s[j] đến e[j] được che phủ và không che phần nào khác. I Yêu cầu: Với mỗi màn trình diễn trong số q màn trình diễn này, hãy giúp Bình xác định xem có tồn tại cấu hình rèm thỏa mãn yêu cầu của anh ấy hay không. Subtask 1 (10%): 1 ≤ n, m, q ≤ 200: O(n ∗ m ∗ q) I Với mỗi truy vấn i, lặp qua tất cả m tấm rèm. Một rèm j có thể được sử dụng nếu s[i] ≤ l[j] và r [j] ≤ e[i]; tức là bức màn nằm hoàn toàn trong phạm vi truy vấn. I Trong số tối đa m rèm có thể được sử dụng để trả lời truy vấn i, hãy đánh dấu riêng từng phần từ l[j] đến r [j] là bị che phủ, sau đó kiểm tra xem mọi phần từ l[j] đến r [j] có bị che không để trả lời truy vấn. Subtask 2 (10%): 1 ≤ n, m, q ≤ 2000 I Thực hiện cùng thao tác lặp để tìm các rèm có thể được sử dụng cho truy vấn i như trong subtask 1. Có nhiều cách để tăng tốc độ này. I Cách dễ nhất là sử dụng bitset để thực hiện trong O(nm/64), cho ĐPT toàn bộ là O(nmq/64). I Cũng có thể thực hiện trong O(m log m) bằng cách lưu trữ các phần được bao phủ dưới dạng tập hợp các phạm vi có thứ tự. Điều này dẫn đến ĐPT toàn bộ là O(qm log m). Subtask 3 (15%): 1 ≤ n ≤ 2000: O(n2 ∗ q) I Nhận xét: có nhiều nhất 2000 phần nghĩa là chỉ có tối đa 20002 phạm vi truy vấn. Như vậy, ta có thể chuẩn bị một số loại tính toán trước cho tất cả các truy vấn có thể có. I Có thể thực hiện điều này trong O(n2 ) bằng QHĐ. Đặt dp[l][r ] bằng 1 nếu một tập con các rèm có thể được chọn để chỉ che các phần [l, r ) và bằng 0 nếu trái lại. I Khởi tạo: đánh dấu dp[l][r ] = 1 nếu tồn tại một tấm màn che chính xác [l, r ) và 0 nếu trái lại. Bây giờ ta cập nhật các giá trị của dp[.][.] theo thứ tự từ điển (tức là (1, 1), (1, 2), ..., (1, n), (2, 1), ...(n, n)). Subtask 3 (15%): 1 ≤ n ≤ 2000: O(n2 ∗ q) I Trong quá trình thực hiện, ta lưu trữ một biến j khác đại diện cho “giá trị lớn nhất i < r sao cho dp[l][i] = 1”. Nếu dp[l][r ] = 1 thì ta cập nhật i thành r , trái lại dp[l][r ] không thay đổi. I Để xác định xem dp[l][r ] = 1, I Nếu có một tấm rèm che [l, r ), rõ ràng dp[l][r ] = 1. I Trái lại, phải tồn tại một số j < r sao cho dp[l][j] = 1 và tồn tại một số i giữa l và j sao cho có một tấm rèm che chính xác [i, r ). Ta không cần kiểm tra tất cả j sao cho dp[l][j] = 1, mà thay vào đó, kiểm tra giá trị j < r lớn nhất với dp[l][j] = 1 là đủ. Ta chỉ cần kiểm tra sự tồn tại i giữa l và j sao cho dp[i][l] = 1. Có thể sử dụng một mảng tổng tiền tố p[i][l] = p[1][l] + ... + p[i][l] để xử lý. Subtask 4 (20%): s[j] = 1: O(n + m + q) I Ta có thể thực hiện quét tương tự như subtask 3. Do s[j] = 1, ta có thể đặt ans[i] bằng 1 nếu các tấm rèm có thể che chính xác [1, i] và bằng 0 nếu trái lại. I Ban đầu, đối với các rèm có l[i] = 1, ta có thể đặt ngay ans[r [i]] thành 1. Ta cũng muốn duy trì cho mỗi điểm cuối bên phải R, giá trị L nhỏ nhất sao cho một rèm [L, R] tồn tại hoặc không có rèm nào kết thúc tại R. I Sau đó, quét i từ 1 đến n, duy trì mức j tối đa hiện tại với j < i và ans[j] = 1. I Sử dụng các kết quả tính toán trước đó, lấy L nhỏ nhất sao cho tồn tại một rèm [L, i]. Nếu L − 1 ≤ j thì ans[i] = 1, trái lại bằng 0. I Để trả lời các truy vấn, chỉ cần kiểm tra xem ans[s[j]] = 1 hay không. Subtask 5 (25%): 1 ≤ n ≤ 100000: O((n + m + q) log2 n) √ I Giới hạn cho thấy ĐPT sẽ tương tự O(n n) hoặc O(n log2 n). I Có thể áp dụng các giải pháp sử dụng các nhóm có kích √ thước n hoặc chia để trị. Dưới đây là giải pháp O(n log2 n). I Xét một khoảng [a, b] và tất cả các rèm có điểm cuối bên trái nằm bên trong [a, b], ký hiệu là S[a,b] và sắp xếp không giảm theo điểm cuối bên phải. Giả sử truy vấn [s, e] bao trùm toàn bộ [a, b]. Khi đó, các rèm trong S[a,b] mà ta có thể sử dụng sẽ tạo thành tiền tố của S[a,b] vì ràng buộc điểm cuối bên trái luôn nằm trong [s, e]. Gọi là S[a,b] (e). I Tập các rèm sử dụng này có thể không che hết [a, b] và cũng có thể che một số phần bên ngoài [a, b]. Subtask 5 (25%): 1 ≤ n ≤ 100000: O((n + m + q) log2 n) I Nhận thấy không có tấm rèm nào như vậy có thể che được phần x < a, và nếu nó che được một phần phần y > b thì tất cả các phần trong [b, y ] cũng được che phủ. Vì vậy, ta có thể liên kết mỗi tiền tố của các tấm rèm trong S[a,b] với: I Chỉ số x lớn nhất trong [a, b] sao cho không có rèm nào ở tiền tố che x. (1) I Chỉ số y lớn nhất > b sao cho có rèm ở tiền tố che phủ y . (2) I Bây giờ hãy xem xét hai khoảng [a, b] và [b + 1, c] cùng với truy vấn [s, e] bao trùm toàn bộ [a, c]. Nếu S[a,b] (e) chứa (1) thì S[b+1,c] (e) sẽ không bao giờ có thể che được nó. Mặt khác, nếu S[b+1,c] (e) chứa (1) thì S[a,b] (e) có thể che được nó tùy thuộc vào (2) của S[a,b] (e) đủ lớn. I Ngoài ra, xét (2) của S[a,b] (e) ∪ S[b+1,c] (e) mở rộng: khi biết các (1) và (2) của hai khoảng liền kề, ta có thể xác định xem liệu hợp của chúng có chứa một (1) và (2) của chúng hay không. Nếu ta có thể chia phạm vi truy vấn thành một số lượng nhỏ các khoảng che phủ phạm vi, thì ta có thể xác định một cách hiệu quả xem liệu phần hợp cuối cùng của các rèm có chứa một (1) hay không. Subtask 5 (25%): 1 ≤ n ≤ 100000: O((n + m + q) log2 n) I Ta cần xây dựng một cây phân đoạn trong đó mỗi nút trong cây phân đoạn lưu trữ các rèm có điểm cuối bên trái nằm trong phạm vi được biểu thị bởi nút đó, được sắp xếp không giảm theo điểm cuối bên phải. Sau đó ta tính toán (1) và (2) của từng tiền tố. Điều này có thể được thực hiện một cách hiệu quả bằng cách duy trì một DSU cho trong mỗi khoảng để kiểm tra phần nào chưa được đề cập, hoặc bằng cách sử dụng ý tưởng sắp xếp trộn để hợp nhất các kết quả từ các nút con với nút cha. I Khi thực hiện một truy vấn, ta chia phạm vi truy vấn thành tối đa 2 log n khoảng bằng cách sử dụng cây phân đoạn. Sau đó, ta sử dụng tìm kiếm nhị phân trên mỗi nút để xác định lỗ chính xác và giá trị tầm với cho từng phân đoạn. Sau đó ta có thể xử lý các phân đoạn từ trái sang phải. Nếu không có (1) nào tồn tại trong phạm vi truy vấn ở cuối thì câu trả lời là có. Trái lại, câu trả lời là không. I Việc xây dựng cây phân đoạn nằm giữa O((n + m) log n) và O((n + m)log 2 n) tùy thuộc vào việc cài đặt. Mỗi truy vấn bao gồm O(log n) tìm kiếm nhị phân, mỗi lần tốn O(log n), do đó tổng số truy vấn là O(q log2 n). Subtask 6 (20%):1 ≤ n, m, q ≤ 500000: O((n + m + q) log n) I Chia để trị (một cách khác sử dụng cây phân đoạn lazy) I Thiết lập hàm đệ quy dnc(X , Y ). Đặt m = b(X + Y )/2c. Trong quá trình dnc(X , Y ), ta sẽ xử lý tất cả các truy vấn j thỏa mãn s[j] ≤ m và e[j] ≥ m + 1. Sau đó, ta sẽ gọi dnc(X , m) và dnc(m + 1, Y ) để xử lý tất cả các truy vấn khác không vượt qua m. I Bây giờ ta sẽ mô tả cách xử lý tất cả các truy vấn thỏa mãn s[j] ≤ m và e[j] ≥ m + 1. Ta sẽ xác định closeL(i) cho X ≤ i ≤ m là j nhỏ nhất trong đó j ≥ m, sao cho có thể che phủ [i, j] một cách chính xác. Tương tự, closeR(j) với m + 1 ≤ i ≤ Y là i lớn nhất trong đó i ≤ m + 1 sao cho có thể bao phủ chính xác [i, j]. Phạm vi truy vấn [s, e] là tốt khi và chỉ khi closeL(s) ≤ e và closeR(e) ≥ s. Subtask 6 (20%):1 ≤ n, m, q ≤ 500000: O((n + m + q) log n) I Bây giờ ta sẽ mô tả cách tính closeL. Khi tính toán closeL(i), hãy xem xét tất cả các rèm có l = i. Trong số đó, chỉ có hai điều là quan trọng, một là r nhỏ nhất ≥ m, và một là r lớn nhất ≤ m − 1. Đối với trường hợp trước, đó chính là giá trị tiềm năng của closeL(i). I Đối với trường hợp thứ hai, ta sẽ cần tìm giá trị tối thiểu của closeL(k) cho i + 1 ≤ k ≤ r + 1. Ta có thể làm như vậy bằng cách duy trì một ngăn xếp đơn lưu trữ các cặp (k, closeL(k)). Ta đưa ra tất cả các giá trị trong ngăn xếp có k ≤ r + 1 và đặt giá trị tối thiểu là closeL(i) cho giá trị đó. Sau đó, ta sẽ biết giá trị nhỏ nhất của closeL(i). Sau đó ta có thể đẩy nó vào ngăn xếp đơn. I Tính toán closeR tương tự. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Bài 1. Lập danh sách – MAKELIST Bài 2. Quy hoạch đường phố – ZONING Bài 3. Lẩu băng chuyền – HOTPOT SUMSET Cho dãy A gồm n phần tử nguyên dương. Yêu cầu: Hãy đếm số tổng khác nhau có thể tạo ra từ các dãy con không nhất thiết liên tiếp của A (không tính dãy rỗng). I Dòng thứ nhất chứa số nguyên dương n miêu tả số phần tử trong dãy (1 ≤ n ≤ 1000). I Dòng thứ hai gồm n phần tử miêu tả dãy A (0 < ai ≤ 1000, ∀i : 1 ≤ i ≤ n). Subtask 1 (30% số điểm): n ≤ 20 I Sinh nhị phân bằng quay lui và dùng đếm phân phối để tính kết quả. I Độ phức tạp: O(2n ). Subtask 2 (40% số điểm): n ≤ 50 I Sử dụng quy hoạch động cái túi, đặt f (i, j) là xét tới i có thể tạo ra được tổng j hay không. Ta có: I f (0, 0) = TRUE . I f (i, j) = f (i − 1, j)|f (i, j − ai ). I Độ phức tạp: O(n × sum(ai )). Subtask 3 (30% số điểm): 1 ≤ n ≤ 1000 Ta vẫn dùng quy hoạch động, nhưng cải tiến bằng bitset: I Đặt bitset < 1000001 > f [n], với bitset f [i] lưu những tổng có thể tạo ra được khi xét tới i (là những bit 1). I Ban đầu tất cả bitset đều có toàn bit 0, riêng f [0][0] = 1. I Ta có f [i] = f [i − 1]|(f [i − 1] << ai ). I Phép toán f [i − 1] << ai có nghĩa là dịch bitset f [i − 1] đi ai bit, bởi nếu ta chọn cộng vào ai , thì ta sẽ tạo ra được các tổng mới từ f [i − 1] bằng cách thêm ai đơn vị. Do chúng ta đang lưu các tổng dưới dạng bit, nên đó chính là phép dịch ai bit. I Kết quả chính là số bit 1 của f [n] trừ đi 1, do ta không tính tập rỗng. Hay chính là f [n].count() − 1. P I Độ phức tạp: O(n × 64ai ), do các thao tác trên bitset nhanh gấp 64 lần so với mảng. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Bài 1. Lập danh sách – MAKELIST Bài 2. Quy hoạch đường phố – ZONING Bài 3. Lẩu băng chuyền – HOTPOT Xếp hình tam giác - TRIANGLE Cho dãy số A độ dài N . Hãy xử lí hai loại truy vấn: I 1 i x: Đổi giá trị Ai thành x. I 2 l r : Đếm số bộ ba chỉ số i < j < k trong đoạn [l, r ] sao cho Ai , Aj , Ak là độ dài ba cạnh của một tam giác. Giới hạn: 1 ≤ N ≤ 3 ∗ 105 , 1 ≤ Q ≤ 105 , 1 ≤ Ai , x ≤ 10. Subtask 1 (N ≤ 100, Q ≤ 10): O(Q ∗ N 3 ) Với mỗi truy vấn loại 2, ta duyệt qua từng bộ ba chỉ số (i, j, k) rồi kiểm tra từng bộ ba. Để kiểm tra xem a, b, c có là độ dài ba cạnh của tam giác hay không, ta cần kiểm tra cạnh lớn nhất có độ dài nhỏ hơn tổng độ dài hai cạnh còn lại hay không. Subtask 2 (N ≤ 105 , Q ≤ 10): O(Q(N + X 3 )) với X là giới hạn trên của Ai Subtask 2 đòi hỏi thuật toán đếm số bộ ba tam giác nhanh hơn, dựa vào giới hạn Ai ≤ 10. Kí hiệu C (n, k) là tổ hợp chập k của n (số cách lấy ra k phần tử từ tập hợp n phần tử không phân biệt thứ tự). Với mỗi truy vấn loại 2, gọi fx là tần số của giá trị x trong đoạn từ vị trí l đến r của dãy A. Ta có thể chia các tam giác cần đếm thành ba loại và đếm riêng chúng như sau: I Tam giác đều có ba cạnh độ dài x: Tăng đáp án thêm C (fx , 3). I Tam giác cân có cạnh đáy độ dài x và hai cạnh còn lại độ dài y (x 6= y ): Kiểm tra điều kiện x < 2 ∗ y , nếu thỏa thì tăng đáp án thêm fx ∗ C (fy , 2). I Tam giác thường có ba cạnh x < y < z: Kiểm tra điều kiện x + y > z, nếu thỏa thì tăng đáp án thêm fx ∗ fy ∗ fz . Subtask 3 (Chỉ có truy vấn loại 2): O(Q ∗ X 3 ) với X là giới hạn trên của Ai I Subtask 3 và subtask 4 đòi hỏi cách tính fx nhanh hơn, rồi từ đó áp dụng thuật toán đếm số tam giác của subtask 2. I Với subtask 3 chỉ có truy vấn loại 2, ta có thể dùng kĩ thuật mảng cộng dồn để tính nhanh các giá trị fx . Cụ thể, gọi d[x][i] là tần số của giá trị x trong các vị trí từ 1 đến i. Khi đó, với truy vấn 2 l r bất kì, ta có fx = d[x][r ] − d[x][l − 1]. I Các bạn có thể đọc thêm về kĩ thuật mảng cộng dồn tại bài viết sau: https://vnoi.info/wiki/algo/data-structures/ prefix-sum-and-difference-array.md Subtask 4 (1 ≤ N ≤ 3 ∗ 105 , 1 ≤ Q ≤ 105 , 1 ≤ Ai , x ≤ 10): O(Q ∗ (log N + X 3 )) Với subtask này, ta sẽ dựng 10 cây phân đoạn hoặc cây BIT (tương ứng với các giá trị từ 1 đến 10 của Ai ). I Với truy vấn 1 i x , ta cần giảm giá trị vị trí i của cây phân đoạn Ai đi 1, đồng thời tăng giá trị tại vị trí i của cây phân đoạn x thêm 1. I Với truy vấn 2 l r , fx sẽ là tổng các phần tử từ vị trí l đến vị trí r của cây phân đoạn x. Các bạn có thể đọc thêm về cây phân đoạn và cây BIT tại các bài viết sau: I Cây phân đoạn (Segment Tree): https://vnoi.info/wiki/algo/data-structures/ segment-tree-basic.md I Cây chỉ mục nhị phân (BIT/Fenwick Tree): https://vnoi.info/wiki/algo/data-structures/fenwick.md Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Bài 1. Lập danh sách – MAKELIST Bài 2. Quy hoạch đường phố – ZONING Bài 3. Lẩu băng chuyền – HOTPOT Dãy xâu con đối xứng – PALINSUB Phong đưa bạn một dãy a chứa các xâu chỉ bao gồm các chữ cái in thường. Nhiệm vụ của bạn là tìm xem có bao dãy con các xâu là một dãy palindrome. Một dãy các xâu được gọi là dãy palindrome nếu xâu được tạo thành bằng cách ghép các xâu lại theo thứ tự tạo thành một xâu đối xứng. Subtask 1 (40 %): n ≤ 10: O(2N ∗ P |ai |) Sinh ra tất cả xâu có thể tạo được rồi kiểm tra. Subtask 2 (30 %): độ dài tất cả các xâu đều bằng 1: O(N 2 ) Dùng QHĐ, gọi dp[x][y ] là số xâu con đối xứng có thể tạo được trong đoạn ax , ax+1 , ..., ay . Nhận xét: I Nếu ax = ay , mọi xâu đối xứng trong [x + 1, y − 1] (kể cả xâu rỗng) đều có thể được mở rộng bằng cách thêm ax và ay ⇒ dp[x + 1][y − 1] I Nếu không chọn mở rộng các xâu còn lại được chọn ở một trong hai đoạn [x + 1, y ] và [x, y − 1] hoặc cả hai. Các xâu được tính hai lần nằm trong đoạn [x + 1][y − 1] ⇒ dp[x][y − 1] + dp[x + 1][y ] − dp[x + 1][y − 1] ⇒ dp[x][y ] = dp[x +1][y −1]∗(a[x] == a[y ])+dp[x][y −1]+dp[x +1][y ]−dp[x +1][y −1] ans = dp[1][n] Cơ sở: dp[x][x] = 2 (tính cả xâu rỗng) và dp[x][y ] = 1 với mọi x > y (xâu rỗng). P Subtask 3 (30 %): n ≤ 3000: O(( |ai |)2 ) I Sử dụng ý tưởng tương tự subtask 2, tuy nhiên mỗi phần tử bây giờ là một xâu thay vì một ký tự. I Vấn đề ở subtask này là khi ta chọn a[x] và a[y ], độ dài của chúng có thể không bằng nhau. Do đó, khi thực hiện so khớp lần lượt, một bên đã hết nhưng bên kia vẫn còn ký tự cần so khớp. Ta cần lưu lại phần còn dư này để thực hiện so khớp tiếp với xâu tiếp theo. I Để đơn giản, ta nối các xâu lại với nhau, thêm ký tự ‘|’ đầu, cuối và ở giữa các xâu để biết bắt đầu/kết thúc của một xâu. Gọi xâu vừa tạo được là s. P Subtask 3 (30 %): n ≤ 3000: O(( |ai |)2 ) Cơ sở: tùy thuộc vào cách cài đặt, có một số lưu ý như sau: I Ta chỉ tính xâu rỗng vào kết quả khi và chỉ khi s[x] = s[y ] = ‘|’. I x > y có thể ứng với nhiều trường hợp khác nhau, ví dụ trạng thái xuất phát từ trường hợp thứ 4 khác trạng thái xuất phát từ trường hợp thứ 2 hoặc 3 trong công thức chuyển trạng thái sau. P Subtask 3 (30 %): n ≤ 3000: O(( |ai |)2 ) Chuyển trạng thái cho dp[x][y ]: I s[x] và s[y ] đều là chữ cái, ta tiếp tục so khớp: dp[x][y ] = dp[x + 1][y − 1] nếu s[x] = s[y ], ngược lại dp[x][y ] = 0. I s[x] là chữ cái, s[y ] = ‘|’, bên phải đã hết nhưng bên trái vẫn còn. Ta cần chọn một xâu tiếp theo cho bên phải (nếu dùng for sẽ bị quá thời gian) bằng cách chọn một trong hai thao tác: bắt đầu so khớp xâu tiếp theo bên phải hoặc bỏ qua nó. ⇒ dp[x][y − 1] + dp[x][nxt] với nxt là vị trí ‘|’ tiếp theo bên trái của y . I s[y ] là chữ cái, s[x] = ‘|’. Tương tự trường hợp trên. I s[x] = s[y ] = ‘|’ tương tự subtask trên, ta có hai cách: chọn cả s[x] và s[y ] để mở rộng hoặc không chọn. ⇒ dp[x +1][y −1]+(dp[nxtX ][y ]+dp[x][nxtY ]−dp[nxtX ][nxtY ]) với nxtX (nxtY ) là vị trí ‘|’ tiếp theo bên phải (trái) của x (y ). Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Lập danh sách – MAKELIST Có N khóa, mỗi khóa có chính xác K học sinh. Mỗi khóa thầy sẽ chọn ra 1 bạn để tham gia vào đội. Một điều đặc biệt tại ngôi trường này là học sinh ở khóa thứ i thì trong tên có chính xác i kí tự. Danh sách chọn các học sinh thoả mãn: I Với mỗi học sinh có tên trong danh sách, thì tên của người này phải xuất hiện dưới dạng xâu con liên tiếp trong tên của tất cả học sinh có tên dài hơn. Yêu cầu: Hãy giúp thầy tổng phụ trách tính số danh sách khác nhau có thể lập ra. Subtask 1 (N ≤ 5, K ≤ 10): O(K N ∗ K ∗ N) Thực hiện duyệt tất cả các cách lập danh sách. Một danh sách thỏa mãn nếu như tên của học sinh ở khóa thứ i phải là 1 xâu con liên tiếp trong tên của học sinh ở khóa thứ i + 1. Subtask 2 (N ≤ 50, K ≤ 100): O(N ∗ K 3 ) Gọi dp[i, j] là số cách lập danh sách khi xét đến khóa thứ i và học sinh thứ j trong khóa đó được chọn. Ta xây dựng công thức quy hoạch động: I dp[i, j]+ = dp[i − 1, j 0 ] nếu tên của học sinh j 0 ở khóa thứ i − 1 là xâu con liên tiếp trong tên của học sinh j ở khóa thứ i. Để kiểm tra tên của học sinh j 0 ở khóa thứ i − 1 là xâu con liên tiếp trong tên của học sinh j ở khóa thứ i hay không thì mất độ phức tạp là O(K ). Subtask 3 (N ≤ 50, K ≤ 1500): O(N ∗ K ∗ log N) Ta sử dụng map < string , int > cnt để đếm những học sinh có tên giống nhau và sử dụng map < string , int > pos để lưu lại vị trí của học sinh trong mỗi khóa (với những học sinh có giống nhau thì ta chỉ cần lưu 1 bạn bất kỳ đại diện). Dễ thấy một xâu S1 S2 ...Si độ dài i chỉ có hai xâu con liên tiếp độ dài i − 1 đó là: I S1 S2 ...Si−1 . I S2 S3 ...Si . Gọi dp[i,j] theo như subtask 2, ta có công thức quy hoạch động: I dp[i, j]+ = dp[i − 1, pos[S1 S2 ...Si−1 ]] ∗ cnt[S1 S2 ...Si−1 ]. I dp[i, j]+ = dp[i − 1, pos[S2 S3 ...Si ]] ∗ cnt[S2 S3 ...Si ]. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Quy hoạch đường phố – ZONING Thành phố bao gồm N thị trấn và M con đường, con đường thứ i kết nối trực tiếp 2 thị trấn ai , bi (ai 6= bi ). Đảm bảo rằng mọi cặp thị trấn có đi đến với nhau thông qua một số con đường. Mỗi con đường đều có một chi phí để bảo trì, có thể xem là trọng số của một cạnh. Kế hoạch đề ra là phải phá hủy một số con đường và chỉ để lại N − 1 con đường sao cho: "Các con đường giữ lại tạo thành một cây khung nhỏ nhất". Hiện tại chi phí bảo trì của các con đường chưa được công bố. Giang là một quản lý của công ty bảo trì đường xá và mong muốn giữ lại một tập hợp các con đường r1 , r2 , ..., rN−1 . Vì thế, Giang sẽ điều chỉnh chi phí bảo trì bằng cách chọn ra một dãy số w1 , w2 , ..., wM là một dãy hoán vị từ 1 đến M thỏa mãn con đường i có chi phí bảo trì là wi . Giang sẽ chọn dãy w1 , w2 , ..., wM có thứ tự từ điển nhỏ nhất sao cho theo kế hoạch của Phong đề ra thì các con đường được giữ lại là các con đường mà Giang mong muốn. Yêu cầu: Hãy giúp Giang xác định dãy số w1 , w2 , ..., wM thỏa mãn để báo cáo lên Phong. Subtask 1 (N, M ≤ 9): O(M! ∗ M ∗ log M) Gọi đồ thị của bài toán là G , một tập cạnh Giang mong muốn là S. Mục tiêu chúng ta là gán hoán vị trọng số nhỏ nhất về mặt từ điển cho các cạnh của G sao cho S là cây khung nhỏ nhất của G . Thực hiện duyệt tất cả các hoán vị, sau đó sử dụng thuật toán Kruskal để tìm và kiểm tra xem tập S có phải là cây khung nhỏ nhất của đồ thị G hay không. Subtask 2 (N, M ≤ 103 ): O(N ∗ M) Tối thiểu hóa w1 , sau đó là w2 , tiếp theo là w3 ,... và đây cũng là ý tưởng chính để giải bài toán này. Xử lý các cạnh theo thứ tự tăng dần của chỉ số như sau: Có 3 trường hợp khi xét đến một cạnh. I Nếu cạnh đã được gán trọng số, ta bỏ qua nó. I Nếu cạnh nằm trong tập S, chúng ta gán cho cạnh đó bởi trọng số nhỏ nhất chưa được sử dụng trên bất kỳ cạnh nào khác. I Nếu cạnh (a, b) không thuộc S, ta xét đường đi từ a đến b dọc theo các cạnh thuộc S. Dễ thấy, tất cả các cạnh nằm trên đường đi từ a đến b thuộc tập S phải được gán trọng số nhỏ hơn trọng số cạnh (a, b). Vì thế ta sẽ gán những trọng số nhỏ nhất chưa được sử dụng lên các cạnh trên đường đi từ a đến b trước, rồi mới gán trọng số nhỏ nhất chưa được sử dụng lên cạnh (a, b). Subtask 2 (N, M ≤ 103 ): O(N ∗ M) Ví dụ với test mẫu: I Đầu tiên xét cạnh (3, 4). Vì cạnh (3, 4) không thuộc trong tập S nên ta xét đường đi từ 3 đến 4 dọc theo các cạnh (4, 1) − (1, 3). Theo cách trên, ta phải gán trọng số 1,2 vào hai cạnh này trước (cạnh nào có thứ tự đứng trước thì gán 1 và cạnh còn lại gán 2). Sau đó ta mới gán trọng số 3 vào cạnh (3,4). I Tiếp theo xét cạnh (1,2), ta gán trọng số là 4. I Tiếp theo xét cạnh (2,3), ta gán trọng số là 5. I Hai cạnh còn lại đã được gán trọng số nên ta bỏ qua. Subtask 3 (ari = 1, bri = i + 1 (kq là đồ thị hình sao)): O(M) Ý tưởng subtask 3, giống với subtask 2. Vì đồ thị của tập S là hình sao nên mọi đường đi (a, b) thông qua S đều chỉ đi qua 2 cạnh. Subtask 4 (ari = i, bri = i + 1): O(M ∗ log M) Đồ thị của tập S là một đường thẳng. Ta đánh số một thứ tự khác từ 1 đến N − 1 vào các cạnh dọc theo đường thẳng đó. Sử dụng một tập set để lưu thứ tự mới những cạnh thuộc tập S và chưa được gán trọng số. Đường đi từ a đến b dọc theo đường thẳng sẽ qua các cạnh có thứ tự liên tiếp nhau l, l + 1, ..., r . Từ tập set, ta lấy ra tất cả những cạnh chưa được gán trọng số và có thứ tự mới nằm trong khoảng [l, r ]. Như vậy mỗi cạnh ta chỉ xử lý đúng một lần. Subtask 5 (M = N, ai = i, bi = i mod N + 1): O(M) Đồ thị G là một chu trình gồm N đỉnh, ta sẽ gán trọng số N vào cạnh không thuộc tập S, các cạnh còn lại gán theo thứ tự xuất hiện. Subtask 6 (M = N): O(M ∗ log M) Đặt cạnh duy nhất không thuộc tập S là (a, b). Gán trọng số theo thứ tự tăng dần cho những cạnh xuất hiện trước (a, b). Tiếp theo gán trọng số cho các cạnh thuộc tập S nằm trên đường đi từ (a, b), sau đó gán trọng số cho cạnh (a, b). Cuối cùng là gán trọng số cho những cạnh còn lại xuất hiện sau (a, b). Subtask 7 (N, M ≤ 3 ∗ 105 ): O(M ∗ log M) Giải pháp cuối cùng liên quan đến việc tối ưu hóa subtask 2. Chúng ta có thể sử dụng cấu trúc dữ liệu DSU để đảm bảo rằng chỉ các cạnh chưa được gắn trọng số mới được liệt kê khi xét đường đi từ a đến b dọc theo các cạnh thuộc tập S. Để làm điều này, ta chọn một đỉnh làm gốc, sau đó DFS để xác định độ sâu của mỗi đỉnh. Khi gán trọng số cho cạnh (u, v ) bất kỳ, ta sử dụng DSU để hợp đỉnh sâu hơn vào đỉnh cao hơn. Khi xét đường đi từ a đến b dọc theo các cạnh thuộc tập S. Ta xuất phát từ 2 đỉnh a, b và di chuyển dần lên trên. Hợp nhất đỉnh a và đỉnh cha của nó, hợp nhất đỉnh b vào đỉnh b của nó, làm tương tự như vậy cho đến khi chúng hợp nhất trong đỉnh cha chung gần nhất thì dừng lại. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Lẩu băng chuyền – HOTPOT I Đất nước có N thành phố được nối liên thông với nhau bởi N − 1 tuyến đường hai chiều. Mỗi tuyến đường đều có một nhà hàng Lẩu băng chuyền, nhà hàng thứ i nằm trên tuyến đường giữa hai thành phố ai và bi có độ thỏa mãn là wi . I Team lên kế hoạch di chuyển từ thành phố x đến thành phố y thỏa mãn nếu quãng đường di chuyển có độ dài là l khi đi qua mỗi thành phố tối đa một lần thì sẽ luôn tồn tại một tuyến đường có nhà hàng Lẩu băng chuyền có độ thỏa mãn là w mà w − l ≥ k. Khi đó, hành trình di chuyển từ thành phố x đến thành phố y được gọi là một hành trình tuyệt vời. I Yêu cầu: Hãy tính số lượng cặp thành phố x và y mà hành trình di chuyển từ thành phố x đến thành phố y là một hành trình tuyệt vời. Subtask 1 (N ≤ 1000): O(N 2 ) Duyệt toàn bộ. Subtask 2 (Thành phố thứ i và i + 1 với mọi i ∈ [1, N − 1] được kết nối với nhau thông qua một tuyến đường): O(N log2 N) I Xem đồ thị là một đường thẳng. Ta sử dụng phương pháp chia để trị để đếm số lượng cặp đỉnh thỏa mãn. I Gọi f (l, r ) là số lượng cặp đỉnh (x, y ) thỏa mãn điều kiện bài toán kèm theo điều kiện l ≤ x < y ≤ r . Đặt m = [(l + r )/2], ta thực hiện chia để trị tính f (l, m) và f (m + 1, r ) sau đó sẽ đếm số cặp (x, y ) thỏa mãn điều kiện bài toán và l ≤ x ≤ m < y ≤ r , hay những cặp (x, y ) đi qua cạnh m ↔ m + 1. I Gọi d(x) là độ dài đường đi từ x → m, v (x) là giá trị w lớn nhất trên các cạnh từ x → m. I Các cặp (x, y ) cần thỏa mãn: max(v (x), v (y )) − (d(x) + d(y )) ≥ k. Giả sử v (x) ≤ v (y ), khi đó điều kiện trở thành v (y ) − (d(x) + d(y )) ≥ k → d(x) ≤ v (y ) − d(y ) − k. Subtask 2 (Thành phố thứ i và i + 1 với mọi i ∈ [1, N − 1] được kết nối với nhau thông qua một tuyến đường): O(N log2 N) I Ta có thể sắp xếp các đỉnh l...r theo v (x). Sau đó ta sẽ sử dụng BIT để đếm các d(x) ≤ v (y ) − d(y ) − k. Giả sử y là đỉnh hiện tại, ta sẽ cộng vào kết quả các BIT lớn hơn d(y ) sau đó update cộng 1 đơn vị lên BIT các giá trị nhỏ hơn v (y ) − d(y ) − k. ĐPT để thực hiện điều này là O(N ∗ log N). I Vấn đề ở đây là điều kiện l ≤ x ≤ m < y ≤ r , ta có thể khắc phục một cách đơn giản đó là trừ đi những cặp (x, y ) thỏa mãn d(x) ≤ v (y ) − d(y ) − k thuộc đoạn [l. . . m] và [m + 1. . . r ]. Subtask 3 (N, k ≤ 105 : O(N log2 N) Dựa trên subtask 2 nhưng giá trị m ở đây chính là centroid của cây. Xây dựng d và v tương tự subtask 2. Thay vì có hai phần, ta phải đi tính những cây con (subtree). Đầu tiên ta sẽ tính kết quả trên cây con gốc m, sau đó trừ đi các kết quả trên các subtree. Sau đó lại tiếp tục đệ quy để tính trên các subtree. Kiến thức về Centroid Tree: I https://oj.vnoi.info/post/72-noobcpp I https://laptrinhthidau.wordpress.com/2016/08/23/ centroid-decomposition-phan-ra-trong-tam-tren-cay/ I https://codeforces.com/blog/entry/73707 Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Độ đơn giản – SIMPLE Có N quả bóng bay được xếp thành hàng ngang và có tổng cộng tối đa 26 màu sắc khác nhau, mỗi màu sắc được biểu diễn bởi một chữ cái tiếng Anh in thường. Cô bé đã nói với ba rằng muốn mua một dãy các quả bóng liên tiếp có độ đơn giản nhỏ nhất. Độ đơn giản của dãy các quả bóng liên tiếp được xác định bằng số lượng màu sắc khác nhau chia cho số lượng quả bóng trong dãy đó. Yêu cầu: Hãy giúp ba Như thực hiện yêu cầu của cô bé. Subtask 1 (N ≤ 100): O(N 3 ) Duyệt các đoạn con (L, R) và tính số màu bóng khác nhau có trong quả bóng L, L + 1, ..., R. Subtask 2 (N ≤ 2000): O(N 2 ) Duyệt các đoạn con (L, R), khi duyệt chỉ số R, ta duy trì biến để đếm số lượng màu bóng khác nhau. Subtask 3 (Chỉ có tối đa 2 màu khác nhau: ‘a’, ‘b’): O(N) Ta có các trường hợp sau: I Nếu chọn dãy chỉ có 1 loại kí tự ‘a’ hoặc ‘b’ thì chọn dãy liên tiếp dài nhất. I Nếu chọn dãy có cả 2 loại kí tự ‘a’ và ‘b’ thì chọn cả xâu. Lấy kết quả từ các trường hợp trên. Subtask 4 (Chỉ có tối đa 5 màu khác nhau: ‘a’, ‘b’, ‘c’, ‘d’, ‘e’): O(25 ∗ N) Ta duyệt 25 trường hợp dãy có thể chứa các kí tự nào. Với mỗi trường hợp ta sử dụng kĩ thuật two-pointer để tìm dãy liên tiếp dài nhất. Subtask 5 (N ≤ 105 ): O(26 ∗ N) Vì số màu tối đa không vượt quá 26 nên ta duyệt K = 1 → 26 là số màu khác nhau. Với mỗi giá trị K , ta sử dụng kĩ thuật two-pointer để tìm dãy liên tiếp dài nhất. Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Nối đường — DSUR I 1 u v : thêm một con đường u − v ; I 2 u v : phá huỷ con đường u − v ; I 3 u v : địa điểm u có đến được v thông qua các con đường đang có hay không? Với mỗi yêu cầu loại 3, hãy đưa ra “YES” nếu câu trả lời là khẳng định và “NO” nếu ngược lại. Subtask 1: n, q ≤ 1000 Duyệt theo chiều rộng BFS Subtask 2: không có truy vấn loại 2 Thực hiện thuần tuý theo cấu trúc dữ liệu các tập không giao nhau: DSU Subtask 3: [q/2] truy vấn đầu tiên chỉ gồm các truy vấn loại 1, q − [q/2] truy vấn sau chỉ gồm các truy vấn loại 2 và 3 I [q/2] truy vấn đầu tiên thực hiện thuần tuý theo cấu trúc dữ liệu các tập không giao nhau DSU. I q − [q/2] truy vấn sau làm đảo ngược lại phép hợp thành phép huỷ đối với các truy vấn loại 2 và trả lời đối với các truy vấn loại 3 Subtask 4: n, q ≤ 105 I Làm theo offline gộp hết các truy vấn lại I Xét các truy vấn thêm và xoá. Mỗi cạnh (u, v ) sẽ xuất hiện trong khoảng thời gian [`, r ], với ` là thời điểm thêm cạnh và r là thời điểm xoá cạnh I Áp dụng phương pháp chia để trị với cấu trúc dữ liệu các tập không giao nhau DSU, lưu ý không dùng phương pháp nén đường 1 2 3 4 DC (L , R , dsu ) { Xét tất cả các truy vấn mà q.l <= L và R <= q.r bởi cây phân đoạn ([L; R] nằm trong [q.l; q.r]) và gọi unite q.u và q.v trong DSU 5 if L == R nếu query[L] là loại 3 thì trả lời và dừng đệ qui, đồng thời quay lui lại DSU trước 6 7 8 9 Mid = ( L + R ) / 2; DC (L , Mid , dsu ); DC ( Mid + 1 , R , dsu ); 10 11 12 13 Quay lui lại DSU trước 14 15 } I ĐPT: O(q log n log q). Bài 1. Xếp hình – PUZZLE Bài 2. Sơn nhà ngày Tết – PAINTHOUSE Bài 3. Du lịch – TRAVEL Bài 1. Cá mập cắn cáp – SHARK Bài 2. Cho cừu vào chuồng – BARNSHEEP Bài 3. Rèm sân khấu – CURTAINS Bài 1. SUMSET Bài 2. TRIANGLE Bài 3. PALINSUB Tàu điện ngầm Thành phố mà Hải đang sống có hệ thống tàu điện ngầm bao gồm N trạm và M tuyến đường ngầm mỗi tuyến nối trực tiếp hai trạm khác nhau. Hai trạm được gọi là kề nhau nếu như có tuyến đường nối trực tiếp hai trạm. Không có 2 tuyến nào nối cùng 1 cặp trạm và giữa hai trạm bất kỳ đều có cách đi giữa chúng thông qua các tuyến đường ngầm. Trạm i có số lượng khách trung bình hàng ngày là pi . Hải mới được giao nhiệm vụ xây dựng và phát triển các chi nhánh công ty quảng cáo của mình tại các trạm tàu điện ngầm. Sau khi khảo sát, Hải nhận thấy rằng hệ thống tàu điện ngầm là có không quá 15 trạm mà mỗi trạm kề với ít nhất 3 trạm khác. Yêu cầu: Giúp Hải xây dựng trên một tập con các trạm sao cho không có hai trạm nào trong tập con được chọn là kề nhau, và tổng số lượng khách trung bình hàng ngày của các trạm trong tập đó phải là lớn nhất. Subtask 1(N ≤ 20): O(2N ) Duyệt toàn bộ tập con. Subtask 2,3: Đồ thị chỉ gồm mạch thẳng hoặc vòng: O(N) Quy hoạch động đơn giản cho bài toán tập độc lập trọng số lớn nhất trên mạch thẳng và mạch vòng. Subtask 4 (100%) Do có không quá 15 đỉnh bậc ≥ 3, các đỉnh còn lại bậc 1 hoặc 2 tạo nên các mạch thẳng và vòng. Chia bài toán thành các phần giải quyết độc lập như sau: I Tách đồ thị thành các phần mạch thẳng và mạch vòng xử lý riêng như trong Subtask 2,3; I Duyệt O(215 ) với phần còn lại cho các đỉnh bậc ≥ 3 (có thể vừa duyệt vừa giải các bài toán mạch thẳng và vòng để xử lý các phần còn liên thông với nhau).