Uploaded by wacacik922

CBL-hint (4)

advertisement
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).
Download