IOI 2018 - Ngày 1

Bộ đề bài

# Bài tập Điểm Thời gian: Giới hạn bộ nhớ
1 IOI 2018 - Combo 100 (p) 1.0s 256M
2 IOI 2018 - Seats 100 (p) 3.0s 256M
3 IOI 2018 - Werewolf 100 (p) 4.0s 512M

1. IOI 2018 - Combo

Điểm: 100 (p) Thời gian: 1.0s Bộ nhớ: 256M Input: bàn phím Output: màn hình

Bạn đang chơi trò chơi video hành động. Bộ điều khiển trò chơi có \(4\) phím, A, B, XY. Trong trò chơi này, bạn giành điểm bằng các tổ hợp nước đi. Bạn có thể thực hiện một tổ hợp nước đi bằng việc ấn các phím theo một dãy.

Trò chơi có một dãy bí mật các phím được biểu diễn bởi xâu ký tự \(S\) chỉ gồm các ký tự trong số \(4\) ký tự này. Bạn không biết xâu \(S\), nhưng biết độ dài của nó là \(N\).

Ký tự đầu tiên của \(S\) không bao giờ xuất hiện lại trong nó. Ví dụ, \(S\) có thể là ABXYY hoặc XYYAA, nhưng không thể là AAAAA hoặc BXYBX.

Bạn được ấn một dãy không quá \(4N\) phím trong một tổ hợp nước đi. Giả sử \(p\) là xâu biểu diễn dãy phím mà bạn bấm. Số điểm đạt được trong nước đi này bằng độ dài của tiền tố dài nhất của \(S\) đồng thời là xâu con của \(p\). Xâu con của một xâu \(t\) là một dãy (có thể rỗng) gồm các ký tự liên tiếp trong \(t\). Tiền tố của \(t\) là xâu con của \(t\) mà nó hoặc là xâu rỗng, hoặc bắt đầu tại ký tự đầu tiên của \(t\).

Chẳng hạn, nếu \(S\)ABXYY\(p\)XXYYABYABXAY, bạn đạt \(3\) điểm vì ABX là tiền tố dài nhất của \(S\) đồng thời là xâu con của \(p\).

Nhiệm vụ của bạn là xác định xâu bí mật \(S\) bằng cách sử dụng ít tổ hợp nước đi.

Chi tiết cài đặt

Bạn cần cài đặt hàm sau:

C++
string guess_sequence(int N)
  • N: độ dài của xâu \(S\).
  • Hàm này được gọi đúng một lần cho mỗi bộ dữ liệu.
  • Hàm này phải trả lại xâu \(S\).

Chương trình của bạn có thể gọi hàm sau:

C++
int press(string p)
  • p: dãy các phím mà bạn bấm.
  • p phải có độ dài từ \(0\) đến \(4N\), kể cả hai đầu mút. Mỗi ký tự của p phải là A, B, X hoặc Y.
  • Bạn không được gọi hàm này quá \(8\,000\) lần cho mỗi bộ dữ liệu.
  • Hàm này trả lại số điểm mà bạn đạt được khi ấn dãy phím biểu diễn bởi p.

Nếu một trong các điều kiện trên không được thỏa mãn, chương trình của bạn được chấm là Wrong Answer. Ngược lại, chương trình được chấm là Accepted và điểm của bạn được tính theo số lần gọi hàm press (xem phần Subtasks).

Trong C++, giao diện trong tệp combo.h của gói đính kèm là:

C++
std::string guess_sequence(int N);
int press(std::string p);

Ví dụ

Giả sử \(S\)ABXYY. Trình chấm gọi guess_sequence(5). Một ví dụ về trao đổi được cho trong bảng sau:

Lời gọi Giá trị trả về
press("XXYYABYABXAY") \(3\)
press("ABXYY") \(5\)
press("ABXYYABXYY") \(5\)
press("") \(0\)
press("X") \(0\)
press("BXYY") \(0\)
press("YYXBA") \(1\)
press("AY") \(1\)

Đối với lần gọi press thứ nhất, ABX xuất hiện trong XXYYABYABXAY như một xâu con, còn ABXY thì không, nên giá trị \(3\) được trả lại.

Đối với lần gọi press thứ ba, toàn bộ ABXYY xuất hiện như một xâu con trong ABXYYABXYY, nên giá trị \(5\) được trả lại.

Đối với lần gọi press thứ sáu, không có tiền tố nào của ABXYY ngoài tiền tố rỗng xuất hiện như một xâu con trong BXYY, nên giá trị \(0\) được trả lại.

Cuối cùng, guess_sequence(5) phải trả lại ABXYY.

Tệp sample-01-in.txt trong gói nén zip đính kèm tương ứng với ví dụ này.

Hạn chế

  • \(1 \le N \le 2\,000\).
  • Mỗi ký tự của \(S\)A, B, X hoặc Y.
  • Ký tự đầu tiên của \(S\) không bao giờ lặp lại trong \(S\).

Trong bài toán này, trình chấm KHÔNG thích nghi. Điều đó có nghĩa là \(S\) được cố định ngay từ đầu khi chạy trình chấm và không phụ thuộc vào các truy vấn do chương trình của bạn đưa ra.

Phân nhóm

Subtask Điểm Hạn chế bổ sung
\(1\) \(5\) \(N = 3\)
\(2\) \(95\) Không có hạn chế bổ sung.

Đối với subtask \(2\), điểm cho mỗi bộ dữ liệu được tính như sau. Gọi \(q\) là số lần gọi hàm press.

\[ \text{điểm} = \begin{cases} 95 & \text{nếu } q \le N + 2, \\ 95 - 3(q - N - 2) & \text{nếu } N + 2 < q \le N + 10, \\ 25 & \text{nếu } N + 10 < q \le 2N + 1, \\ 5 & \text{nếu } \max\{N + 10, 2N + 1\} < q \le 4N, \\ 0 & \text{trong các trường hợp còn lại.} \end{cases} \]

Điểm của bạn cho mỗi subtask là điểm nhỏ nhất trong số các điểm của các bộ dữ liệu thuộc subtask đó.

Trình chấm mẫu

Trình chấm mẫu đọc dữ liệu vào theo khuôn dạng sau:

  • Dòng \(1\): \(S\).

Nếu chương trình của bạn được chấm là Accepted, trình chấm mẫu in ra Accepted: q, trong đó q là số lần gọi hàm press.

Nếu chương trình của bạn được chấm là Wrong Answer, trình chấm mẫu in ra Wrong Answer: MSG. Ý nghĩa của MSG như sau:

  • invalid press: giá trị p truyền cho press không hợp lệ. Cụ thể, độ dài của p không nằm trong khoảng từ \(0\) đến \(4N\), kể cả hai đầu mút, hoặc có ký tự trong p không phải là A, B, X hay Y.
  • too many moves: hàm press được gọi nhiều hơn \(8\,000\) lần.
  • wrong guess: giá trị trả về của guess_sequence không trùng với xâu \(S\).

Dữ liệu vào của ví dụ trên:

ABXYY

Với đúng chuỗi \(8\) lời gọi press trong bảng ví dụ và giá trị trả về ABXYY của guess_sequence, trình chấm mẫu in ra:

Accepted: 8

2. IOI 2018 - Seats

Điểm: 100 (p) Thời gian: 3.0s Bộ nhớ: 256M Input: bàn phím Output: màn hình

Bạn đang tổ chức kỳ thi lập trình quốc tế trong một hội trường hình chữ nhật gồm \(HW\) ghế ngồi, được bố trí thành \(H\) hàng và \(W\) cột. Các hàng được đánh số từ \(0\) đến \(H-1\), các cột được đánh số từ \(0\) đến \(W-1\). Ghế ở hàng \(r\) và cột \(c\) được ký hiệu là \((r,c)\). Bạn mời \(HW\) thí sinh được đánh số từ \(0\) đến \(HW-1\). Bạn tạo ra sơ đồ chỗ ngồi, xếp thí sinh \(i\) (\(0 \le i \le HW-1\)) vào ghế \((R_i,C_i)\). Trong sơ đồ chỗ ngồi, mỗi thí sinh ngồi ở đúng một ghế và mỗi ghế có đúng một thí sinh.

Một tập hợp \(S\) các ghế trong hội trường được gọi là có dạng hình chữ nhật nếu tồn tại các số nguyên \(r_1\), \(r_2\), \(c_1\)\(c_2\) thỏa mãn các điều kiện sau:

  • \(0 \le r_1 \le r_2 \le H-1\).
  • \(0 \le c_1 \le c_2 \le W-1\).
  • \(S\) chính là tập hợp tất cả các ghế \((r,c)\) thỏa mãn \(r_1 \le r \le r_2\)\(c_1 \le c \le c_2\).

Một tập hợp có dạng hình chữ nhật gồm \(k\) ghế (\(1 \le k \le HW\)) được gọi là đẹp nếu các thí sinh ngồi trên các ghế thuộc tập này có số thứ tự từ \(0\) đến \(k-1\). Độ đẹp của một sơ đồ chỗ ngồi là số lượng các tập hợp có dạng hình chữ nhật đẹp trong sơ đồ.

Sau khi tạo ra sơ đồ chỗ ngồi, bạn nhận được một số yêu cầu hoán chuyển ghế ngồi của hai thí sinh. Có \(Q\) yêu cầu, đánh số từ \(0\) đến \(Q-1\) theo thứ tự thời điểm xuất hiện. Yêu cầu \(j\) (\(0 \le j \le Q-1\)) thực hiện việc hoán chuyển chỗ ngồi của hai thí sinh \(A_j\)\(B_j\). Bạn thực hiện ngay mỗi yêu cầu và cập nhật sơ đồ chỗ ngồi. Sau mỗi lần cập nhật, bạn cần tính độ đẹp của sơ đồ chỗ ngồi hiện tại.

Chi tiết cài đặt

Bạn phải cài đặt thủ tục và hàm sau:

C++
give_initial_chart(int H, int W, int[] R, int[] C)
  • H, W: số lượng hàng và số lượng cột.
  • R, C: các mảng có độ dài \(HW\), biểu diễn sơ đồ chỗ ngồi ban đầu.
  • Thủ tục này được gọi đúng một lần và trước mọi lần gọi swap_seats.
C++
int swap_seats(int a, int b)
  • Hàm này mô tả một yêu cầu hoán chuyển hai ghế ngồi.
  • a, b: số thứ tự của hai thí sinh có chỗ ngồi bị hoán chuyển.
  • Hàm này được gọi \(Q\) lần.
  • Hàm này phải trả về độ đẹp của sơ đồ chỗ ngồi sau khi thực hiện hoán chuyển.

Trong C++, giao diện trong tệp seats.h của gói đính kèm là:

C++
void give_initial_chart(int H, int W, std::vector<int> R, std::vector<int> C);
int swap_seats(int a, int b);

Ví dụ

Giả sử \(H=2\), \(W=3\), \(R=[0,1,1,0,0,1]\), \(C=[0,0,1,1,2,2]\)\(Q=2\).

Trình chấm đầu tiên gọi give_initial_chart(2, 3, [0, 1, 1, 0, 0, 1], [0, 0, 1, 1, 2, 2]).

Sơ đồ chỗ ngồi ban đầu như sau; mỗi ô ghi số thứ tự của thí sinh ngồi ở ghế đó.

Hàng / Cột \(0\) \(1\) \(2\)
\(0\) \(0\) \(3\) \(4\)
\(1\) \(1\) \(2\) \(5\)

Giả sử trình chấm gọi swap_seats(0, 5). Sau khi thực hiện yêu cầu \(0\), sơ đồ chỗ ngồi như sau:

Hàng / Cột \(0\) \(1\) \(2\)
\(0\) \(5\) \(3\) \(4\)
\(1\) \(1\) \(2\) \(0\)

Các tập hợp ghế tương ứng với các thí sinh \(\{0\}\), \(\{0,1,2\}\)\(\{0,1,2,3,4,5\}\) có dạng hình chữ nhật và đẹp. Do đó, độ đẹp của sơ đồ chỗ ngồi là \(3\), và swap_seats phải trả lại \(3\).

Giả sử trình chấm lại gọi swap_seats(0, 5). Sau khi thực hiện yêu cầu \(1\), sơ đồ chỗ ngồi trở về trạng thái ban đầu. Các tập hợp ghế tương ứng với các thí sinh \(\{0\}\), \(\{0,1\}\), \(\{0,1,2,3\}\)\(\{0,1,2,3,4,5\}\) có dạng hình chữ nhật và đẹp. Do đó, độ đẹp của sơ đồ chỗ ngồi này là \(4\), và swap_seats phải trả lại \(4\).

Hai tệp sample-01-in.txtsample-01-out.txt trong gói zip đính kèm tương ứng với ví dụ này. Các ví dụ dữ liệu vào/ra khác cũng nằm trong gói này.

Hạn chế

  • \(1 \le H\).
  • \(1 \le W\).
  • \(HW \le 1\,000\,000\).
  • \(0 \le R_i \le H-1\) với \(0 \le i \le HW-1\).
  • \(0 \le C_i \le W-1\) với \(0 \le i \le HW-1\).
  • \((R_i,C_i) \ne (R_j,C_j)\) với \(0 \le i < j \le HW-1\).
  • \(1 \le Q \le 50\,000\).
  • \(0 \le a \le HW-1\) cho mọi lần gọi swap_seats.
  • \(0 \le b \le HW-1\) cho mọi lần gọi swap_seats.
  • \(a \ne b\) cho mọi lần gọi swap_seats.

Phân nhóm

Subtask Điểm Hạn chế bổ sung
\(1\) \(5\) \(HW \le 100\), \(Q \le 5\,000\).
\(2\) \(6\) \(HW \le 10\,000\), \(Q \le 5\,000\).
\(3\) \(20\) \(H \le 1\,000\), \(W \le 1\,000\), \(Q \le 5\,000\).
\(4\) \(6\) \(Q \le 5\,000\), \(\lvert a-b\rvert \le 10\,000\) cho mọi lần gọi swap_seats.
\(5\) \(33\) \(H=1\).
\(6\) \(30\) Không có hạn chế bổ sung.

Trình chấm mẫu

Trình chấm mẫu đọc dữ liệu vào theo khuôn dạng sau:

  • Dòng \(1\): \(H\ W\ Q\).
  • Dòng \(2+i\) (\(0 \le i \le HW-1\)): \(R_i\ C_i\).
  • Dòng \(2+HW+j\) (\(0 \le j \le Q-1\)): \(A_j\ B_j\).

Ở đây, \(A_j\)\(B_j\) là các tham số khi gọi swap_seats đối với yêu cầu \(j\).

Trình chấm mẫu in ra câu trả lời của bạn theo khuôn dạng sau:

  • Dòng \(1+j\) (\(0 \le j \le Q-1\)): giá trị trả về của swap_seats đối với yêu cầu \(j\).

Dữ liệu vào của ví dụ trên:

2 3 2
0 0
1 0
1 1
0 1
0 2
1 2
0 5
0 5

Dữ liệu ra tương ứng:

3
4

3. IOI 2018 - Werewolf

Điểm: 100 (p) Thời gian: 4.0s Bộ nhớ: 512M Input: bàn phím Output: màn hình

Quận Ibaraki ở Nhật Bản có \(N\) thành phố và \(M\) con đường. Các thành phố được đánh số từ \(0\) đến \(N-1\) theo thứ tự tăng dần về dân số. Mỗi con đường nối một cặp hai thành phố khác nhau và có thể di chuyển theo cả hai hướng. Bạn có thể sử dụng một hoặc nhiều con đường này để di chuyển từ một thành phố bất kỳ đến một thành phố bất kỳ khác.

Bạn lên kế hoạch gồm \(Q\) chuyến đi, được đánh số từ \(0\) đến \(Q-1\). Chuyến đi \(i\) (\(0 \le i \le Q-1\)) sẽ di chuyển từ thành phố \(S_i\) đến thành phố \(E_i\).

Bạn là người sói. Bạn có hai hình dáng: hình dáng ngườihình dáng sói. Tại thời điểm bắt đầu mỗi chuyến đi, bạn ở hình dáng người. Kết thúc chuyến đi, bạn bắt buộc phải ở hình dáng sói. Trong chuyến đi, bạn phải chuyển đổi (chuyển từ hình dáng người sang hình dáng sói) đúng một lần. Bạn chỉ có thể chuyển đổi khi đang ở một thành phố nào đó, kể cả \(S_i\) hoặc \(E_i\).

Cuộc sống của người sói không dễ dàng. Bạn phải tránh ở hình dáng người khi ở các thành phố ít dân và tránh ở hình dáng sói khi ở các thành phố đông dân. Với mỗi chuyến đi \(i\) (\(0 \le i \le Q-1\)), có hai ngưỡng \(L_i\)\(R_i\) (\(0 \le L_i \le R_i \le N-1\)) chỉ ra các thành phố phải tránh. Cụ thể, bạn không được ở hình dáng người khi ở các thành phố \(0,1,\ldots,L_i-1\), và không được ở hình dáng sói khi ở các thành phố \(R_i+1,R_i+2,\ldots,N-1\). Điều đó có nghĩa là trong chuyến đi \(i\), bạn chỉ được chuyển đổi hình dáng ở một trong các thành phố \(L_i,L_i+1,\ldots,R_i\).

Nhiệm vụ của bạn là xác định, đối với mỗi chuyến đi, có tồn tại cách di chuyển từ thành phố \(S_i\) đến thành phố \(E_i\) thỏa mãn tất cả các điều kiện trên hay không. Đường đi của bạn có thể có độ dài bất kỳ.

Chi tiết cài đặt

Bạn cần cài đặt hàm sau:

C++
int[] check_validity(int N, int[] X, int[] Y, int[] S, int[] E, int[] L, int[] R)
  • N: số lượng thành phố.
  • X, Y: các mảng có độ dài \(M\). Với mỗi \(j\) (\(0 \le j \le M-1\)), thành phố X[j] được nối trực tiếp với thành phố Y[j] bởi một con đường.
  • S, E, L, R: các mảng có độ dài \(Q\), biểu diễn các chuyến đi.

Chú ý rằng \(M\)\(Q\) là độ dài của các mảng; cách lấy độ dài mảng được mô tả trong phần Lưu ý cài đặt.

Hàm check_validity được gọi đúng một lần cho mỗi bộ dữ liệu. Hàm này phải trả về một mảng số nguyên \(A\) có độ dài \(Q\). Giá trị \(A_i\) (\(0 \le i \le Q-1\)) phải là \(1\) nếu chuyến đi \(i\) có thể thực hiện được và thỏa mãn các điều kiện đã nêu, hoặc \(0\) trong trường hợp ngược lại.

Trong C++, giao diện trong tệp werewolf.h của gói đính kèm là:

C++
std::vector<int> check_validity(int N, std::vector<int> X, std::vector<int> Y,
                                std::vector<int> S, std::vector<int> E,
                                std::vector<int> L, std::vector<int> R);

Ví dụ

Cho \(N=6\), \(M=6\), \(Q=3\), \(X=[5,1,1,3,3,5]\), \(Y=[1,2,3,4,0,2]\), \(S=[4,4,5]\), \(E=[2,2,4]\), \(L=[1,2,3]\)\(R=[2,2,4]\).

Trình chấm gọi check_validity(6, [5, 1, 1, 3, 3, 5], [1, 2, 3, 4, 0, 2], [4, 4, 5], [2, 2, 4], [1, 2, 3], [2, 2, 4]).

Với chuyến đi \(0\), bạn có thể di chuyển từ thành phố \(4\) đến thành phố \(2\) như sau:

  1. Bắt đầu tại thành phố \(4\) (bạn ở hình dáng người).
  2. Di chuyển đến thành phố \(3\) (bạn ở hình dáng người).
  3. Di chuyển đến thành phố \(1\) (bạn ở hình dáng người).
  4. Chuyển đổi từ hình dáng người sang hình dáng sói (bạn ở hình dáng sói).
  5. Di chuyển đến thành phố \(2\) (bạn ở hình dáng sói).

Với chuyến đi \(1\)\(2\), bạn không thể di chuyển giữa các thành phố đã cho mà thỏa mãn các điều kiện.

Do đó, chương trình của bạn phải trả lại \([1,0,0]\).

Các tệp sample-01-in.txtsample-01-out.txt trong gói nén zip đính kèm tương ứng với ví dụ này. Gói đính kèm còn chứa một cặp tệp dữ liệu vào/ra mẫu khác.

Hạn chế

  • \(2 \le N \le 200\,000\).
  • \(N-1 \le M \le 400\,000\).
  • \(1 \le Q \le 200\,000\).
  • Với mỗi \(0 \le j \le M-1\): \(0 \le X_j \le N-1\), \(0 \le Y_j \le N-1\)\(X_j \ne Y_j\).
  • Bạn có thể di chuyển từ một thành phố bất kỳ đến một thành phố bất kỳ khác bằng cách sử dụng các con đường.
  • Mỗi cặp thành phố được nối trực tiếp với nhau bởi nhiều nhất một con đường. Nói cách khác, với mọi \(0 \le j < k \le M-1\), \((X_j,Y_j) \ne (X_k,Y_k)\)\((Y_j,X_j) \ne (X_k,Y_k)\).
  • Với mỗi \(0 \le i \le Q-1\): \(0 \le L_i \le S_i \le N-1\), \(0 \le E_i \le R_i \le N-1\), \(S_i \ne E_i\)\(L_i \le R_i\).

Phân nhóm

Subtask Điểm Hạn chế bổ sung
\(1\) \(7\) \(N \le 100\), \(M \le 200\), \(Q \le 100\).
\(2\) \(8\) \(N \le 3\,000\), \(M \le 6\,000\), \(Q \le 3\,000\).
\(3\) \(34\) \(M=N-1\) và mỗi thành phố là đầu mút của nhiều nhất \(2\) con đường (các thành phố được nối thành một đường).
\(4\) \(51\) Không có hạn chế bổ sung.

Trình chấm mẫu

Trình chấm mẫu đọc dữ liệu vào theo khuôn dạng sau:

  • Dòng \(1\): \(N\ M\ Q\).
  • Dòng \(2+j\) (\(0 \le j \le M-1\)): \(X_j\ Y_j\).
  • Dòng \(2+M+i\) (\(0 \le i \le Q-1\)): \(S_i\ E_i\ L_i\ R_i\).

Trình chấm mẫu in ra giá trị trả về của check_validity theo khuôn dạng sau:

  • Dòng \(1+i\) (\(0 \le i \le Q-1\)): \(A_i\).

Dữ liệu vào của ví dụ trên:

6 6 3
5 1
1 2
1 3
3 4
3 0
5 2
4 2 1 2
4 2 2 2
5 4 3 4

Dữ liệu ra tương ứng:

1
0
0