| # | Bài tập | Điểm | Thời gian: | Giới hạn bộ nhớ |
|---|---|---|---|---|
| 1 | APIO 2025 - Hack! | 100 (p) | 3.0s | 1G |
| 2 | APIO 2025 - Permutation Game | 100 (p) | 2.0s | 1G |
| 3 | APIO 2025 - Rotating Lines | 100 (p) | 3.0s | 1G |
Sau một giờ thi Codeforces, bạn nhận thấy một thí sinh khác trong phòng đã giải được một bài bằng unordered_set. Đã đến lúc hack!
Bạn biết rằng unordered_set sử dụng một bảng băm gồm \(n\) bucket, được đánh số từ \(0\) đến \(n-1\). Bạn không biết \(n\) và muốn xác định giá trị này.
Khi chèn số nguyên \(x\) vào bảng băm, nó được đưa vào bucket thứ \((x \bmod n)\). Nếu bucket đó đang có \(b\) phần tử thì phép chèn gây ra \(b\) xung đột băm.
Với một truy vấn gồm \(k\) số nguyên phân biệt \(x[0],x[1],\ldots,x[k-1]\), trình tương tác trả về tổng số xung đột phát sinh khi lần lượt chèn chúng vào một unordered_set rỗng. Chi phí của truy vấn là \(k\).
Ví dụ, nếu \(n=5\) và \(x=[2,15,7,27,8,30]\) thì có tổng cộng \(4\) xung đột:
| Thao tác | Xung đột mới | Các bucket |
|---|---|---|
| Ban đầu | - | [], [], [], [], [] |
| Chèn \(x[0]=2\) | 0 | [], [], [2], [], [] |
| Chèn \(x[1]=15\) | 0 | [15], [], [2], [], [] |
| Chèn \(x[2]=7\) | 1 | [15], [], [2, 7], [], [] |
| Chèn \(x[3]=27\) | 2 | [15], [], [2, 7, 27], [], [] |
| Chèn \(x[4]=8\) | 0 | [15], [], [2, 7, 27], [8], [] |
| Chèn \(x[5]=30\) | 1 | [15, 30], [], [2, 7, 27], [8], [] |
Mỗi truy vấn sử dụng một unordered_set rỗng mới, nên các truy vấn hoàn toàn độc lập.
Hãy tìm \(n\) với tổng chi phí không quá \(1\,000\,000\).
Bạn cần cài đặt hàm:
int hack()
Hàm phải trả về giá trị bí mật \(n\). Trong một test, trình chấm có thể gọi hack() nhiều lần; mỗi lời gọi là một kịch bản độc lập.
Trong hack(), bạn có thể gọi:
long long collisions(std::vector<long long> x)
x là một mảng các số nguyên phân biệt, với \(1\le x[i]\le 10^{18}\).x vào một unordered_set rỗng.hack(), tổng độ dài của x qua tất cả các truy vấn không được vượt quá \(1\,000\,000\).Vì hack() có thể được gọi nhiều lần, hãy lưu ý dữ liệu còn lại từ lời gọi trước, đặc biệt là trạng thái trong các biến toàn cục.
Giới hạn chi phí \(1\,000\,000\) áp dụng riêng cho từng kịch bản. Nếu có \(t\) lời gọi hack(), tổng chi phí có thể đạt \(t\cdot 1\,000\,000\), nhưng mỗi lời gọi vẫn không được vượt quá \(1\,000\,000\).
Trình tương tác không thích ứng: các giá trị \(n\) được cố định trước khi quá trình tương tác bắt đầu.
Giả sử có hai kịch bản. Trong lời gọi hack() đầu tiên, bạn thực hiện:
| Lời gọi | Giá trị trả về |
|---|---|
collisions([2, 15, 7, 27, 8, 30]) |
4 |
collisions([1, 2, 3]) |
0 |
collisions([10, 20, 30, 40, 50]) |
10 |
Từ đó, nếu xác định được \(n=5\), hack() phải trả về \(5\).
Trong lời gọi hack() tiếp theo, giả sử bạn thực hiện:
| Lời gọi | Giá trị trả về |
|---|---|
collisions([1, 3]) |
1 |
collisions([2, 4]) |
1 |
Giá trị \(n\) duy nhất phù hợp là \(2\), nên hack() phải trả về \(2\).
collisions().Trong subtask 3, bạn có thể nhận điểm thành phần. Gọi \(q\) là tổng chi phí lớn nhất của một lời gọi hack() trong tất cả các test thuộc subtask. Điểm của subtask được tính như sau:
| Điều kiện | Điểm |
|---|---|
| \(q>1\,000\,000\) | \(0\) |
| \(110\,000<q\le 1\,000\,000\) | \(75\cdot\log_{50}\left(\dfrac{10^6}{q-90\,000}\right)\) |
| \(q\le 110\,000\) | \(75\) |
Nếu trong bất kỳ test nào, lời gọi collisions() vi phạm các ràng buộc ở trên hoặc hack() trả về sai, lời giải nhận \(0\) điểm cho subtask đó.
Trình chấm mẫu đọc:
t
n_1
n_2
...
n_t
Với mỗi kịch bản, gọi \(m\) là giá trị hack() trả về và \(c\) là tổng chi phí truy vấn. Trình chấm mẫu in:
m c
Bạn có thể tải trình chấm mẫu và tệp tiêu đề ở phần đính kèm.
Nguồn: APIO 2025, bài 1 - Hack!.
Alice và Bob là bạn thời thơ ấu và rất thích các trò chơi trí tuệ. Hôm nay, họ chơi một trò chơi mới trên đồ thị.
Bộ trò chơi gồm một đồ thị liên thông có \(m\) đỉnh, đánh số từ \(0\) đến \(m-1\), và \(e\) cạnh, đánh số từ \(0\) đến \(e-1\). Cạnh thứ \(i\) nối hai đỉnh \(u[i]\) và \(v[i]\).
Bộ trò chơi còn có một hoán vị \(p[0],p[1],\ldots,p[n-1]\) độ dài \(n\), trong đó \(m\le n\). Điểm của \(p\) là số chỉ số \(i\) thỏa mãn \(p[i]=i\).
Trò chơi kéo dài tối đa \(10^{100}\) lượt. Trong mỗi lượt:
Alice muốn tối đa hóa điểm cuối cùng của hoán vị, còn Bob muốn tối thiểu hóa nó. Điểm tối ưu là điểm cuối cùng nếu cả hai cùng chơi tối ưu.
Bạn cần xác định chính xác điểm tối ưu, sau đó chơi với Bob đủ số lượt để đạt điểm ít nhất bằng điểm tối ưu. Chiến lược của Alice phải đúng với mọi cách đi của Bob, kể cả khi Bob không đi tối ưu.
Bạn cần cài đặt hàm:
int Alice(int m, int e, std::vector<int> u, std::vector<int> v,
int n, std::vector<int> p)
m: số đỉnh của đồ thị.e: số cạnh của đồ thị.u, v: hai mảng độ dài \(e\) mô tả các cạnh.n: độ dài hoán vị.p: hoán vị ban đầu, có độ dài \(n\).Trong hàm này, bạn có thể gọi:
int Bob(std::vector<int> t)
t phải có đúng \(m\) phần tử phân biệt, mỗi phần tử thuộc \([0,n-1]\).Xét lời gọi:
Alice(5, 6, [4, 0, 3, 1, 4, 2], [2, 2, 0, 2, 0, 3],
10, [8, 2, 7, 6, 1, 5, 0, 9, 3, 4])
Đồ thị tương ứng:
Ban đầu, \(p=[8,2,7,6,1,5,0,9,3,4]\). Có thể chứng minh điểm tối ưu là \(1\).
Giả sử Alice thực hiện bốn lượt minh họa sau:
t truyền cho Bob |
Giá trị Bob trả về |
Hai chỉ số của p |
p sau phép đổi |
|---|---|---|---|
[3, 1, 5, 2, 0] |
5 | 5, 2 | [8, 2, 5, 6, 1, 7, 0, 9, 3, 4] |
[9, 3, 7, 2, 1] |
0 | 1, 7 | [8, 9, 5, 6, 1, 7, 0, 2, 3, 4] |
[5, 6, 7, 8, 9] |
1 | 5, 7 | [8, 9, 5, 6, 1, 2, 0, 7, 3, 4] |
[7, 5, 2, 3, 6] |
3 | 5, 2 | [8, 9, 2, 6, 1, 5, 0, 7, 3, 4] |
Các lượt trên chỉ để minh họa, Alice và Bob không nhất thiết đi tối ưu. Alice cũng có thể dừng ngay từ đầu vì điểm ban đầu đã bằng \(1\).
Sau bốn lượt, điểm thực tế là \(3\) vì \(p[2]=2\), \(p[5]=5\), \(p[7]=7\). Tuy vậy, Alice() vẫn phải trả về điểm tối ưu \(1\). Trả về \(3\) sẽ bị chấm sai.
Mỗi subtask có thể cho điểm thành phần. Gọi \(k\) là số lượt, tức số lần gọi Bob(), và gọi \(r\) là giá trị lớn nhất của \(k/n\) trên tất cả các test trong subtask. Điểm subtask được nhân với hệ số:
| Điều kiện | Hệ số |
|---|---|
| \(r\ge 12\) | \(0\) |
| \(3<r<12\) | \(1-\log_{10}(r-2)\) |
| \(r\le 3\) | \(1\) |
Do đó, dùng không quá \(3n\) lượt sẽ nhận trọn điểm subtask. Dùng từ \(12n\) lượt trở lên sẽ nhận \(0\) điểm và được hiển thị là kết quả sai.
Trình chấm mẫu đọc:
m e
u[0] v[0]
u[1] v[1]
...
u[e-1] v[e-1]
n
p[0] p[1] ... p[n-1]
Trình chấm mẫu in lần lượt hoán vị cuối, giá trị Alice() trả về, điểm thực tế của hoán vị cuối và số lượt đã dùng.
Bạn có thể tải trình chấm mẫu và tệp tiêu đề ở phần đính kèm.
Nguồn: APIO 2025, bài 2 - Permutation Game.
Asadullo là một nhà nghiên cứu xuất sắc tại APIO (Alliance for Power and Industrial Optimization). Ông đang nghiên cứu cách tạo năng lượng bằng một loại vật liệu chưa từng được biết đến.
Có \(n\) thanh rất dài làm từ vật liệu này, được mô tả bởi mảng \(v[0],v[1],\ldots,v[n-1]\). Thanh thứ \(i\) nằm ở góc
so với chiều dương của trục \(x\), theo chiều ngược kim đồng hồ.
Hiệu suất năng lượng của các thanh là
trong đó \(\operatorname{acute}(i,j)\) là góc nhọn giữa thanh \(i\) và thanh \(j\); trong bài này, góc \(90^{\circ}\) cũng được coi là góc nhọn. Cụ thể,
Ví dụ, với \(v=[5000,12500,37500]\) thì \(a=[18,45,135]\) độ:
Ta có \(\operatorname{acute}(0,1)=7500\) (tức \(27^{\circ}\)), \(\operatorname{acute}(0,2)=17500\) (tức \(63^{\circ}\)), và \(\operatorname{acute}(1,2)=25000\) (tức \(90^{\circ}\)). Hiệu suất bằng \(50000\).
Asadullo muốn xoay các thanh để tối đa hóa hiệu suất, với các hạn chế:
Hãy giúp Asadullo đạt hiệu suất năng lượng lớn nhất có thể.
Bạn cần cài đặt hàm:
void energy(int n, std::vector<int> v)
n: số thanh.v: mảng độ dài \(n\) mô tả vị trí ban đầu của các thanh.Trong hàm này, bạn có thể gọi:
void rotate(std::vector<int> t, int x)
t là mảng các chỉ số phân biệt: \(0\le t[i]<n\) và \(t[i]\ne t[j]\) với mọi \(i<j\). Không yêu cầu t được sắp xếp.t theo tham số x: với mỗi \(i\) thuộc t, \(v[i]\) trở thành \((v[i]+x)\bmod 50000\).t qua tất cả các lời gọi không được vượt quá \(2\,000\,000\).Xét lời gọi:
energy(2, [20000, 10000])
Ban đầu \(v=[20000,10000]\) và hiệu suất bằng \(10000\). Một kịch bản hợp lệ:
rotate([0, 1], 8000). Khi đó \(v=[28000,18000]\) và hiệu suất không đổi.rotate([0], 15000). Khi đó \(v=[43000,18000]\) và hiệu suất trở thành \(25000\).\(25000\) là hiệu suất lớn nhất có thể cho đầu vào này.
Xét lời gọi:
energy(3, [5000, 12500, 37500])
Đây là cấu hình trong hình phía trên. Hiệu suất ban đầu đã lớn nhất, nên không cần thao tác nào.
Trình chấm mẫu đọc:
n
v[0] v[1] ... v[n-1]
Trình chấm mẫu in hiệu suất năng lượng cuối cùng. Ngoài ra, thông tin chi tiết về các thao tác xoay được ghi vào log.txt.
Bạn có thể tải trình chấm mẫu và tệp tiêu đề ở phần đính kèm.
Nguồn: APIO 2025, bài 3 - Rotating Lines.