| # | Bài tập | Điểm | Thời gian: | Giới hạn bộ nhớ |
|---|---|---|---|---|
| 1 | APIO 2023 - Cyberland | 100 (p) | 8.0s | 2G |
| 2 | APIO 2023 - Sequence | 100 (p) | 1.5s | 2G |
| 3 | APIO 2023 - Alice, Bob and Circuit | 100 (p) | 10.0s | 2G |
Năm 3742 đã đến, và bây giờ đến lượt Cyberland đăng cai APIO. Trong thế giới này có \(N\) quốc gia, được đánh số từ \(0\) đến \(N-1\), cùng với \(M\) con đường hai chiều, được đánh số từ \(0\) đến \(M-1\). Con đường thứ \(i\) (\(0\le i<M\)) nối hai quốc gia khác nhau \(x[i]\) và \(y[i]\), và cần \(c[i]\) đơn vị thời gian để đi hết con đường.
Tất cả thí sinh dự APIO đã đến Cyberland, ngoại trừ người đến từ quốc gia của bạn. Bạn sống ở quốc gia \(0\), còn Cyberland là quốc gia \(H\). Là người thông minh nhất đất nước, bạn được giao nhiệm vụ xác định thời gian nhỏ nhất để đi từ quốc gia của mình đến Cyberland.
Một số quốc gia có khả năng đặc biệt đưa tổng thời gian bạn đã đi về \(0\). Một số quốc gia khác có khả năng đặc biệt chia đôi tổng thời gian bạn đã đi (divide-by-2). Bạn có thể ghé thăm một quốc gia nhiều lần. Mỗi lần đến một quốc gia, bạn có thể chọn có sử dụng khả năng đặc biệt tại đó hay không, nhưng chỉ được sử dụng khả năng ấy nhiều nhất một lần trong lần ghé thăm đó. Vì vậy, nếu ghé thăm một quốc gia nhiều lần, bạn có thể dùng khả năng của quốc gia ấy nhiều lần.
Để tránh bị Tổ chức Hóa học Cyberland bắt, trong toàn bộ hành trình bạn chỉ được dùng khả năng divide-by-2 nhiều nhất \(K\) lần. Ngay khi đến Cyberland, bạn không được tiếp tục di chuyển vì kỳ thi APIO sắp bắt đầu.
Cho mảng arr có độ dài \(N\), trong đó \(arr[i]\) (\(0\le i<N\)) mô tả khả năng đặc biệt của quốc gia \(i\):
Luôn có \(arr[0]=arr[H]=1\); nói cách khác, quốc gia của bạn và Cyberland không có khả năng đặc biệt.
Hãy tìm thời gian nhỏ nhất để đến Cyberland. Nếu không thể đến Cyberland, kết quả phải là \(-1\).
Bạn cần cài đặt hàm sau:
double solve(int N, int M, int K, int H,
std::vector<int> x, std::vector<int> y,
std::vector<int> c, std::vector<int> arr);
N: số quốc gia.M: số con đường hai chiều.K: số lần tối đa được sử dụng khả năng divide-by-2.H: chỉ số của quốc gia Cyberland.x, y, c: ba mảng độ dài \(M\); bộ ba \((x[i],y[i],c[i])\) biểu diễn con đường vô hướng thứ \(i\), nối \(x[i]\) với \(y[i]\) và có thời gian di chuyển \(c[i]\).arr: mảng độ dài \(N\); arr[i] mô tả khả năng đặc biệt của quốc gia \(i\).Giả sử giá trị do thí sinh trả về là \(ans_1\) và đáp án chính xác là \(ans_2\). Kết quả được coi là đúng khi và chỉ khi
Do hàm có thể được gọi nhiều lần, bạn phải bảo đảm dữ liệu còn lại từ một lần gọi trước không ảnh hưởng đến lần gọi hiện tại.
Submission phải sử dụng khai báo trong cyberland.h, chỉ cài đặt hàm trên và không tự cài đặt main hay đọc/ghi trực tiếp qua standard input/output.
Ví dụ 1
Xét lời gọi:
solve(3, 2, 30, 2, {1, 2}, {2, 0}, {12, 4}, {1, 2, 1});
Đường đi duy nhất đến Cyberland là \(0\to2\), vì sau khi đến Cyberland bạn không thể di chuyển tiếp. Thời gian được tính như sau:
| Quốc gia | Thời gian đã đi |
|---|---|
| \(0\) | \(0\) |
| \(2\) | \(0+4\to4\) (cộng thời gian) \(\to4\) (khả năng đặc biệt) |
Vì vậy, hàm phải trả về \(4\).
Ví dụ 2
Xét lời gọi:
solve(4, 4, 30, 3, {0, 0, 1, 2}, {1, 2, 3, 3}, {5, 4, 2, 4}, {1, 0, 2, 1});
Có hai đường đi từ quốc gia của bạn đến Cyberland: \(0\to1\to3\) và \(0\to2\to3\).
Nếu đi theo \(0\to1\to3\), thời gian được tính như sau:
| Quốc gia | Thời gian đã đi |
|---|---|
| \(0\) | \(0\) |
| \(1\) | \(0+5\to5\) (cộng thời gian) \(\to0\) (khả năng đặc biệt) |
| \(3\) | \(0+2\to2\) (cộng thời gian) \(\to2\) (khả năng đặc biệt) |
Nếu đi theo \(0\to2\to3\), thời gian được tính như sau:
| Quốc gia | Thời gian đã đi |
|---|---|
| \(0\) | \(0\) |
| \(2\) | \(0+4\to4\) (cộng thời gian) \(\to2\) (khả năng đặc biệt) |
| \(3\) | \(2+4\to6\) (cộng thời gian) \(\to6\) (khả năng đặc biệt) |
Vì vậy, hàm phải trả về \(2\).
| Subtask | Điểm | Ràng buộc bổ sung |
|---|---|---|
| \(1\) | \(5\) | \(N\le3\), \(K\le30\). |
| \(2\) | \(8\) | \(M=N-1\), \(K\le30\), \(arr[i]=1\); có thể đi giữa mọi cặp quốc gia qua \(M\) con đường. |
| \(3\) | \(13\) | \(M=N-1\), \(K\le30\), \(arr[i]\in\{0,1\}\); có thể đi giữa mọi cặp quốc gia qua \(M\) con đường. |
| \(4\) | \(19\) | \(M=N-1\), \(K\le30\), \(x[i]=i\), \(y[i]=i+1\). |
| \(5\) | \(7\) | \(K\le30\), \(arr[i]=1\). |
| \(6\) | \(16\) | \(K\le30\), \(arr[i]\in\{0,1\}\). |
| \(7\) | \(29\) | \(K\le30\). |
| \(8\) | \(3\) | Không có ràng buộc bổ sung. |
Trình chấm mẫu đọc dữ liệu theo khuôn dạng sau:
Với mỗi trường hợp thử, trình chấm mẫu in trên một dòng giá trị mà hàm solve trả về.
Phần này chỉ mô tả giao diện của trình chấm mẫu; submission vẫn phải cài đặt hàm solve như ở trên.
Kho đề và dữ liệu chính thức APIO 2023, bài Cyberland. Gói nguồn được phát hành theo giấy phép CC0 1.0.
Trong thế giới đầy mê hoặc của APIO có một học sinh trẻ tuổi và thông minh tên Alice. Alice rất thích giải những bài toán thú vị thử thách năng lực toán học. Một ngày nọ, cô bắt gặp một dãy số bí ẩn có độ dài \(N\),
và muốn khám phá những bí mật của nó.
Trước hết, ta đưa ra một số định nghĩa. Ký hiệu
là số lần giá trị \(x\) xuất hiện trong đoạn \(A[l],A[l+1],\ldots,A[r]\). Ở đây, \(I[P]\) bằng \(1\) nếu mệnh đề \(P\) đúng và bằng \(0\) nếu \(P\) sai.
Với một dãy số nguyên không rỗng \(B[0],B[1],\ldots,B[k-1]\), gọi \(S(\{B[0],B[1],\ldots,B[k-1]\})\) là tập các trung vị của dãy. Tập này được xác định như sau:
Khi đó
Ví dụ:
Ký hiệu \(S(l,r)\) là tập các trung vị của đoạn \(A[l],A[l+1],\ldots,A[r]\). Alice muốn tìm giá trị lớn nhất của
trên mọi cặp chỉ số \(0\le l\le r\le N-1\). Alice đã có đáp án và nhờ bạn viết chương trình để kiểm chứng đáp án đó.
Bạn cần cài đặt hàm sau:
int sequence(int N, std::vector<int> A);
N: độ dài của dãy A.A: mảng độ dài \(N\) mô tả dãy.Submission phải sử dụng khai báo trong sequence.h, chỉ cài đặt hàm trên và không tự cài đặt main hay đọc/ghi trực tiếp qua standard input/output.
Ví dụ 1
Xét lời gọi:
sequence(7, {1, 2, 3, 1, 2, 1, 3});
Hàm phải trả về \(3\).
Trong trường hợp này, \(S(0,5)=\{1,2\}\), \(W(0,5,1)=3\) và \(W(0,5,2)=2\). Vì vậy giá trị ứng với cặp \((0,5)\) là \(3\). Có thể kiểm chứng rằng đây là giá trị lớn nhất trên mọi cặp hợp lệ.
Ví dụ 2
Xét lời gọi:
sequence(9, {1, 1, 2, 3, 4, 3, 2, 1, 1});
Hàm phải trả về \(2\).
Ví dụ 3
Xét lời gọi:
sequence(14, {2, 6, 2, 5, 3, 4, 2, 1, 4, 3, 5, 6, 3, 2});
Hàm phải trả về \(3\).
| Subtask | Điểm | Ràng buộc bổ sung |
|---|---|---|
| \(1\) | \(11\) | \(N\le100\). |
| \(2\) | \(17\) | \(N\le2\times10^3\). |
| \(3\) | \(7\) | Tồn tại \(x\) sao cho với mọi \(0\le i<x\), \(A[i]\le A[i+1]\), và với mọi \(x<i<N\), \(A[i]\le A[i-1]\). |
| \(4\) | \(12\) | \(A[i]\le3\). |
| \(5\) | \(13\) | \(W(0,N-1,A[i])\le2\) với mọi \(0\le i\le N-1\). |
| \(6\) | \(22\) | \(N\le8\times10^4\). |
| \(7\) | \(18\) | Không có ràng buộc bổ sung. |
Trình chấm mẫu đọc dữ liệu theo khuôn dạng sau:
Trình chấm mẫu in trên một dòng giá trị mà hàm sequence trả về.
Phần này chỉ mô tả giao diện của trình chấm mẫu; submission vẫn phải cài đặt hàm sequence như ở trên.
Kho đề và dữ liệu chính thức APIO 2023, bài Sequence. Gói nguồn được phát hành theo giấy phép CC0 1.0.
Tổ chức Cyberland Circuit có \(n\) thành viên. Mỗi thành viên có một tên riêng duy nhất và một con số yêu thích; các con số yêu thích không nhất thiết khác nhau.
Có \(m\) bức thư đã được gửi giữa các thành viên. Mỗi bức thư có một người gửi và một người nhận, còn nội dung bức thư chính là con số yêu thích của người gửi.
Mỗi thành viên cộng nội dung của tất cả thư mình nhận được, rồi lấy phần dư theo \(65536=2^{16}\) để thu được số kết quả của mình. Nhiệm vụ của bạn là xác định số kết quả của mọi thành viên.
Tuy nhiên, Alice, Bob và Circuit quyết định giải bài toán theo một cách phức tạp hơn:
Cổng là phần tử cơ bản của mạch. Tùy loại, một cổng có không đầu vào hoặc có hai đầu vào boolean, và luôn có một đầu ra boolean. Có hai loại cổng: cổng dữ liệu vào và cổng tính toán.
Cổng dữ liệu vào không có đầu vào và biểu diễn các bit trong chuỗi nhị phân của Alice và Bob.
Cổng tính toán có hai đầu vào và biểu diễn quá trình tính toán.
Công thức trên xác định đầy đủ cả \(16\) loại phép toán:
| \(p\) | \(f(p,x_0,x_1)\) | Diễn giải |
|---|---|---|
| \(0\) | \(0\) | hằng sai |
| \(1\) | \(\neg(x_0\lor x_1)\) | NOR |
| \(2\) | \(x_0>x_1\) | \(x_0\land\neg x_1\) |
| \(3\) | \(\neg x_1\) | NOT \(x_1\) |
| \(4\) | \(x_0<x_1\) | \(\neg x_0\land x_1\) |
| \(5\) | \(\neg x_0\) | NOT \(x_0\) |
| \(6\) | \(x_0\mathbin{\mathrm{XOR}}x_1\) | XOR |
| \(7\) | \(\neg(x_0\land x_1)\) | NAND |
| \(8\) | \(x_0\land x_1\) | AND |
| \(9\) | \(x_0=x_1\) | bằng nhau |
| \(10\) | \(x_0\) | lấy đầu vào thứ nhất |
| \(11\) | \(x_0\ge x_1\) | \(x_0\lor\neg x_1\) |
| \(12\) | \(x_1\) | lấy đầu vào thứ hai |
| \(13\) | \(x_0\le x_1\) | \(\neg x_0\lor x_1\) |
| \(14\) | \(x_0\lor x_1\) | OR |
| \(15\) | \(1\) | hằng đúng |
Một số phép toán thường dùng có bảng chân trị sau:
| \(x_0\) | \(x_1\) | AND, \(f(8,x_0,x_1)\) | OR, \(f(14,x_0,x_1)\) | XOR, \(f(6,x_0,x_1)\) | NOT \(x_0\), \(f(5,x_0,x_1)\) |
|---|---|---|---|---|---|
| \(0\) | \(0\) | \(0\) | \(0\) | \(0\) | \(1\) |
| \(1\) | \(0\) | \(0\) | \(1\) | \(1\) | \(0\) |
| \(0\) | \(1\) | \(0\) | \(1\) | \(1\) | \(1\) |
| \(1\) | \(1\) | \(1\) | \(1\) | \(0\) | \(0\) |
Mọi chỉ số mảng đều bắt đầu từ \(0\). Chẳng hạn, với mảng a có độ dài \(n\), các phần tử hợp lệ là a[0] đến a[n-1]; truy cập ngoài phạm vi có thể gây lỗi. Mọi chuỗi ký tự đều kết thúc bằng ký tự null \0.
Submission phải sử dụng các khai báo trong abc.h, cài đặt đủ ba hàm dưới đây và không tự cài đặt main hay giao tiếp trực tiếp qua standard input/output. Alice, Bob và Circuit được chấm như các vai trò tách biệt; thông tin duy nhất mà hàm circuit nhận trực tiếp là độ dài hai chuỗi nhị phân.
Alice
int alice(const int n, const char names[][5],
const unsigned short numbers[], bool outputs_alice[]);
n: số thành viên, \(0\le n\le700\).names: mảng độ dài \(n\) chứa tên các thành viên. Mọi tên đều khác nhau, chỉ gồm chữ cái tiếng Anh viết thường và dài nhiều nhất \(4\) ký tự.numbers: mảng độ dài \(n\) chứa số yêu thích của từng thành viên; mỗi số nằm trong đoạn từ \(0\) đến \(65535\).outputs_alice: ghi chuỗi nhị phân độ dài \(l_A\) mà Alice gửi cho Circuit.Bob
int bob(const int m, const char senders[][5],
const char recipients[][5], bool outputs_bob[]);
m: số bức thư, \(0\le m\le1000\).senders: mảng độ dài \(m\) chứa tên người gửi của từng bức thư.recipients: mảng độ dài \(m\) chứa tên người nhận của từng bức thư.senders và recipients đều xuất hiện trong đầu vào names của Alice.outputs_bob: ghi chuỗi nhị phân độ dài \(l_B\) mà Bob gửi cho Circuit.Circuit
Để quá trình tính toán thật sự là một mạch tổng quát, hàm circuit không được nhận trực tiếp hai chuỗi nhị phân của Alice và Bob. Hàm chỉ biết độ dài của chúng và phải mô tả cấu trúc mạch.
int circuit(const int la, const int lb, int operations[],
int operands[][2], int outputs_circuit[][16]);
la và lb lần lượt là \(l_A\) và \(l_B\).operations có độ dài \(l\); operations[g] là loại phép toán từ \(0\) đến \(15\) của cổng tính toán mang nhãn \(g\).operands có độ dài \(l\); operands[g][0] và operands[g][1] là nhãn hai cổng đầu vào của cổng \(g\), và cả hai nhãn phải nhỏ hơn \(g\).outputs_circuit có độ dài \(n\). outputs_circuit[i][j] là nhãn cổng tạo ra bit thứ \(j\), đếm từ bit có trọng số thấp nhất, trong số kết quả cuối cùng của thành viên thứ \(i\). Thứ tự thành viên giống thứ tự trong đầu vào của Alice. Mọi nhãn đầu ra phải nằm trong đoạn từ \(0\) đến \(l-1\).Bạn có thể sửa các phần tử của operations và operands có chỉ số nhỏ hơn \(l_A+l_B\), nhưng trình chấm sẽ bỏ qua các sửa đổi này vì đó là các cổng dữ liệu vào.
Xét hai lời gọi:
alice(3, {"alic", "bob", "circ"},
{10000, 20000, 30000}, outputs_alice);
bob(5, {"alic", "bob", "bob", "circ", "circ"},
{"circ", "circ", "alic", "circ", "circ"}, outputs_bob);
Các lời gọi này biểu diễn tình huống sau:
alic có số yêu thích \(10000\), bob có số yêu thích \(20000\), và circ có số yêu thích \(30000\). Một kết quả có thể có của alice là trả về \(2\), tức \(l_A=2\), đồng thời đặt outputs_alice[0] = 1 và outputs_alice[1] = 0, tạo thành chuỗi 10.alic đến circ, v.v. Một kết quả có thể có của bob là trả về \(3\), tức \(l_B=3\), đồng thời đặt outputs_bob[0] = 1, outputs_bob[1] = 1, outputs_bob[2] = 0, tạo thành chuỗi 110.Từ hai độ dài trên, Circuit được gọi như sau:
circuit(2, 3, operations, operands, outputs_circuit);
Một kết quả đúng của hàm này là trả về \(7\), tức thêm hai cổng tính toán mang nhãn \(5\) và \(6\), và thiết lập:
operations = {-1, -1, -1, -1, -1, 8, 14};
operands = {
{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1},
{0, 4}, {2, 5}
};
outputs_circuit = {
{5, 5, 5, 5, 5, 6, 5, 5, 5, 6, 6, 6, 5, 5, 6, 5},
{5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5},
{5, 5, 5, 5, 6, 5, 5, 6, 6, 6, 6, 6, 6, 5, 6, 5}
};
Trong operations và operands, giá trị \(-1\) chỉ biểu diễn thông tin bị bỏ qua ở các cổng dữ liệu vào. Quá trình tính toán là:
outputs_circuit[0] biểu diễn số kết quả của alic:`alic` chỉ nhận một bức thư từ `bob`, nên kết quả đúng là $20000$.
bob không nhận bức thư nào nên kết quả là \(0\).circ làTệp mẫu abc.cpp trong gói đính kèm vượt qua ví dụ này, nhưng không được bảo đảm sẽ vượt qua các trường hợp thử khác.
Trong mọi trường hợp thử:
names ở đầu vào của Alice.alice và bob đều có giới hạn bộ nhớ \(2048\ \mathrm{MiB}\) và giới hạn thời gian \(0{,}02\) giây.circuit có giới hạn bộ nhớ \(2048\ \mathrm{MiB}\) và giới hạn thời gian \(7\) giây.Trong lần chấm chính thức, alice và bob có thể được gọi nhiều lần trong cùng một trường hợp thử. Giới hạn \(0{,}02\) giây áp dụng riêng cho mỗi lần gọi. Điều kiện độ dài cố định của \(l_A\) theo \(n\) và của \(l_B\) theo \(m\) vẫn phải được thỏa mãn giữa các lần gọi.
Có chín subtask, được chia thành ba loại. Các điều kiện trong bảng dưới đây là đầy đủ đối với từng subtask, ngoài các ràng buộc chung đã nêu trên.
| Subtask | Loại | Điểm | Điều kiện |
|---|---|---|---|
| \(1\) | A | \(4\) | \(n=1\), \(m=0\). |
| \(2\) | A | \(4\) | \(n=1\), \(0\le m\le1\). |
| \(3\) | A | \(4\) | \(n=1\), \(0\le m\le1000\). |
| \(4\) | B | \(24\) | \(0\le n\le30\), \(\frac n2\le m\le n^2\); không có hai thư nào có cùng cặp (người gửi, người nhận); mỗi thành viên xuất hiện ít nhất một lần trong đầu vào của Bob với vai trò người gửi hoặc người nhận; \(n=26\); mọi tên là một chữ cái thường và xuất hiện trong đầu vào Alice theo thứ tự a, b, ..., z. |
| \(5\) | B | \(24\) | \(0\le n\le30\), \(\frac n2\le m\le n^2\); không có hai thư nào có cùng cặp (người gửi, người nhận); mỗi thành viên xuất hiện ít nhất một lần trong đầu vào của Bob với vai trò người gửi hoặc người nhận; \(n=26\). |
| \(6\) | B | \(6\) | \(0\le n\le30\), \(\frac n2\le m\le n^2\); không có hai thư nào có cùng cặp (người gửi, người nhận); mỗi thành viên xuất hiện ít nhất một lần trong đầu vào của Bob với vai trò người gửi hoặc người nhận; không có điều kiện bổ sung khác. |
| \(7\) | C | \(18\) | \(0\le n\le700\), \(0\le m\le1000\); \(n=676\); mọi tên là hai chữ cái thường và xuất hiện trong đầu vào Alice theo thứ tự từ điển: aa, ab, ac, ..., az, ba, ..., bz, ca, ..., zz. |
| \(8\) | C | \(10\) | \(0\le n\le700\), \(0\le m\le1000\); \(n=676\). |
| \(9\) | C | \(6\) | \(0\le n\le700\), \(0\le m\le1000\); không có điều kiện bổ sung khác. |
Tổng điểm của loại A là \(12\), loại B là \(54\), và loại C là \(34\).
Trình chấm mẫu đọc dữ liệu theo khuôn dạng sau:
names[i] và numbers[i].senders[i] và recipients[i].Nếu chương trình kết thúc thành công, trình chấm mẫu in \(n\) dòng, mỗi dòng chứa số kết quả cuối cùng do các hàm của bạn tính cho một thành viên. Nếu không, trình chấm không ghi gì ra standard output và ghi thông báo lỗi vào tệp abc.log trong thư mục hiện tại. Trình chấm mẫu còn ghi các giá trị \(l_A\), \(l_B\), \(l\) và thời gian chạy của từng hàm vào abc.log.
Trình chấm mẫu không kiểm tra giới hạn bộ nhớ, cũng không kiểm tra điều kiện rằng với cùng \(n\) hoặc \(m\), các độ dài tương ứng \(l_A\) hoặc \(l_B\) phải bằng nhau. Những điều kiện này vẫn được kiểm tra khi chấm chính thức.
Phần này chỉ mô tả giao diện của trình chấm mẫu; submission vẫn phải cài đặt ba hàm trong abc.h như ở trên.
Kho đề và dữ liệu chính thức APIO 2023, bài Alice, Bob and Circuit. Gói nguồn được phát hành theo giấy phép CC0 1.0.