IOI 2020 - Ngày 1

Bộ đề bài

# Bài tập Điểm Thời gian: Giới hạn bộ nhớ
1 IOI 2020 - Comparing Plants 100 (p) 4.0s 2G
2 IOI 2020 - Connecting Supertrees 100 (p) 1.0s 2G
3 IOI 2020 - Carnival Tickets 100 (p) 2.0s 2G

1. IOI 2020 - Comparing Plants

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

Nhà thực vật học Hazel đến tham quan một triển lãm đặc biệt tại Vườn Bách thảo Singapore. Trong triển lãm, \(n\) cây có chiều cao đôi một khác nhau được đặt trên một vòng tròn. Các cây được gán nhãn từ \(0\) đến \(n-1\) theo chiều kim đồng hồ, với cây \(n-1\) nằm bên cạnh cây \(0\).

Với mỗi cây \(i\) (\(0 \le i \le n-1\)), Hazel so sánh cây \(i\) với từng cây trong \(k-1\) cây kế tiếp theo chiều kim đồng hồ, rồi ghi lại số \(r[i]\) là số cây cao hơn cây \(i\) trong số \(k-1\) cây đó. Như vậy, mỗi giá trị \(r[i]\) phụ thuộc vào tương quan chiều cao của một số cây liên tiếp.

Ví dụ, giả sử \(n=5\), \(k=3\)\(i=3\). Khi đó, \(k-1=2\) cây kế tiếp theo chiều kim đồng hồ tính từ cây \(i=3\) là cây \(4\) và cây \(0\). Nếu cây \(4\) cao hơn cây \(3\) và cây \(0\) thấp hơn cây \(3\), Hazel sẽ ghi lại \(r[3]=1\).

Bạn có thể giả sử Hazel đã ghi các giá trị \(r[i]\) chính xác. Do đó, tồn tại ít nhất một cách gán các chiều cao đôi một khác nhau cho các cây phù hợp với những giá trị này.

Bạn được yêu cầu so sánh chiều cao của \(q\) cặp cây. Đáng tiếc, bạn không thể đến triển lãm. Nguồn thông tin duy nhất của bạn là sổ ghi chép của Hazel, chứa giá trị \(k\) và dãy \(r[0],\ldots,r[n-1]\).

Với mỗi cặp cây khác nhau \(x\)\(y\) cần so sánh, hãy xác định trường hợp nào trong ba trường hợp sau xảy ra:

  • Cây \(x\) chắc chắn cao hơn cây \(y\): với mọi cách gán chiều cao đôi một khác nhau \(h[0],\ldots,h[n-1]\) phù hợp với mảng \(r\), luôn có \(h[x]>h[y]\).
  • Cây \(x\) chắc chắn thấp hơn cây \(y\): với mọi cách gán chiều cao đôi một khác nhau \(h[0],\ldots,h[n-1]\) phù hợp với mảng \(r\), luôn có \(h[x]<h[y]\).
  • Không thể kết luận: không thuộc trường hợp nào trong hai trường hợp trên.

Chi tiết cài đặt

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

C++
void init(int k, std::vector<int> r);
  • \(k\): số cây liên tiếp có chiều cao quyết định mỗi giá trị \(r[i]\).
  • \(r\): mảng kích thước \(n\), trong đó \(r[i]\) là số cây cao hơn cây \(i\) trong \(k-1\) cây kế tiếp theo chiều kim đồng hồ.
  • Hàm này được gọi đúng một lần, trước mọi lời gọi compare_plants.
C++
int compare_plants(int x, int y);
  • \(x\), \(y\): nhãn của hai cây cần so sánh.
  • Hàm phải trả về \(1\) nếu cây \(x\) chắc chắn cao hơn cây \(y\); \(-1\) nếu cây \(x\) chắc chắn thấp hơn cây \(y\); \(0\) nếu không thể kết luận.
  • Hàm này được gọi đúng \(q\) lần.

Dữ liệu vào

Trình chấm mẫu đọc dữ liệu theo định dạng sau:

  • Dòng \(1\): n k q.
  • Dòng \(2\): r[0] r[1] ... r[n-1].
  • Dòng \(3+i\) (\(0 \le i \le q-1\)): x y cho lời gọi compare_plants thứ \(i\) (đánh số từ \(0\)).

Dữ liệu ra

Trình chấm mẫu in trên dòng \(1+i\) (\(0 \le i \le q-1\)) giá trị trả về của lời gọi compare_plants thứ \(i\).

Ràng buộc

  • \(2 \le k \le n \le 200\,000\).
  • \(1 \le q \le 200\,000\).
  • \(0 \le r[i] \le k-1\) (\(0 \le i \le n-1\)).
  • \(0 \le x < y \le n-1\).

Tồn tại ít nhất một cách gán các chiều cao đôi một khác nhau cho các cây phù hợp với mảng \(r\).

Phân nhóm

Nhóm Điểm Ràng buộc bổ sung
1 5 \(k=2\).
2 14 \(n \le 5000\)\(2\cdot k>n\).
3 13 \(2\cdot k>n\).
4 17 Đáp án đúng của mỗi lời gọi compare_plants\(1\) hoặc \(-1\).
5 11 \(n \le 300\)\(q \le \frac{n\cdot(n-1)}{2}\).
6 15 \(x=0\) trong mỗi lời gọi compare_plants.
7 25 Không có ràng buộc bổ sung.

Ví dụ

Ví dụ 1

Lời gọi
init(3, [0, 1, 1, 2])
Note

Giả sử trình chấm gọi compare_plants(0, 2). Vì \(r[0]=0\), ta suy ra ngay cây \(2\) không cao hơn cây \(0\). Do đó, lời gọi phải trả về \(1\).

Giả sử tiếp theo trình chấm gọi compare_plants(1, 2). Trong mọi cách gán chiều cao phù hợp với các ràng buộc trên, cây \(1\) thấp hơn cây \(2\). Do đó, lời gọi phải trả về \(-1\).

Ví dụ 2

Lời gọi
init(2, [0, 1, 0, 1])
Note

Giả sử trình chấm gọi compare_plants(0, 3). Vì \(r[3]=1\), ta biết cây \(0\) cao hơn cây \(3\). Do đó, lời gọi phải trả về \(1\).

Giả sử tiếp theo trình chấm gọi compare_plants(1, 3). Hai cách gán chiều cao \([3,1,4,2]\)\([3,2,4,1]\) đều phù hợp với các phép đo của Hazel. Cây \(1\) thấp hơn cây \(3\) trong một cách gán và cao hơn cây \(3\) trong cách gán còn lại, nên lời gọi phải trả về \(0\).

Nguồn

IOI 2020, Ngày 1 — Comparing Plants (plants). Đề chính thức tiếng Anh và bản dịch tiếng Việt.

2. IOI 2020 - Connecting Supertrees

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

Gardens by the Bay là một công viên thiên nhiên rộng lớn ở Singapore. Trong công viên có \(n\) tòa tháp, được gọi là siêu cây (supertree). Các tháp được gán nhãn từ \(0\) đến \(n-1\). Ta muốn xây dựng một tập gồm không hoặc nhiều cầu. Mỗi cầu nối hai tháp khác nhau và có thể đi qua theo cả hai chiều. Không được có hai cầu nối cùng một cặp tháp.

Một đường đi từ tháp \(x\) đến tháp \(y\) là một dãy gồm một hoặc nhiều tháp sao cho:

  • Phần tử đầu tiên của dãy là \(x\).
  • Phần tử cuối cùng của dãy là \(y\).
  • Tất cả các phần tử của dãy đôi một khác nhau.
  • Mỗi hai phần tử (tháp) liên tiếp trong dãy được nối với nhau bởi một cầu.

Lưu ý rằng theo định nghĩa, có đúng một đường đi từ một tháp đến chính nó; số đường đi khác nhau từ tháp \(i\) đến tháp \(j\) bằng số đường đi khác nhau từ tháp \(j\) đến tháp \(i\).

Kiến trúc sư chính phụ trách thiết kế muốn các cầu được xây dựng sao cho với mọi \(0 \le i,j \le n-1\), có đúng \(p[i][j]\) đường đi khác nhau từ tháp \(i\) đến tháp \(j\), trong đó \(0 \le p[i][j] \le 3\).

Hãy xây dựng một tập các cầu thỏa mãn yêu cầu của kiến trúc sư, hoặc xác định rằng không thể thực hiện được.

Chi tiết cài đặt

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

C++
int construct(std::vector<std::vector<int>> p);
  • \(p\): mảng kích thước \(n\times n\) biểu diễn yêu cầu của kiến trúc sư.
  • Nếu có thể xây dựng, hàm phải gọi build (mô tả bên dưới) đúng một lần để báo cáo phương án xây dựng, rồi trả về \(1\).
  • Nếu không thể xây dựng, hàm phải trả về \(0\)không gọi build lần nào.
  • Hàm construct được gọi đúng một lần.

Hàm build được định nghĩa như sau:

C++
void build(std::vector<std::vector<int>> b);
  • \(b\): mảng kích thước \(n\times n\), với \(b[i][j]=1\) nếu có cầu nối tháp \(i\) và tháp \(j\), và \(b[i][j]=0\) nếu không có.
  • Mảng phải thỏa mãn:
\[ b[i][j]=b[j][i] \qquad (0 \le i,j \le n-1), \]
\[ b[i][i]=0 \qquad (0 \le i \le n-1). \]

Dữ liệu vào

Trình chấm mẫu đọc dữ liệu theo định dạng sau:

  • Dòng \(1\): n.
  • Dòng \(2+i\) (\(0 \le i \le n-1\)): p[i][0] p[i][1] ... p[i][n-1].

Dữ liệu ra

Trình chấm mẫu in:

  • Dòng \(1\): giá trị trả về của construct.

Nếu giá trị trả về của construct\(1\), trình chấm mẫu in thêm:

  • Dòng \(2+i\) (\(0 \le i \le n-1\)): b[i][0] b[i][1] ... b[i][n-1].

Ràng buộc

  • \(1 \le n \le 1000\).
  • \(p[i][i]=1\) (\(0 \le i \le n-1\)).
  • \(p[i][j]=p[j][i]\) (\(0 \le i,j \le n-1\)).
  • \(0 \le p[i][j] \le 3\) (\(0 \le i,j \le n-1\)).

Phân nhóm

Nhóm Điểm Ràng buộc bổ sung
1 11 \(p[i][j]=1\) với mọi \(0 \le i,j \le n-1\).
2 10 \(p[i][j]=0\) hoặc \(1\) với mọi \(0 \le i,j \le n-1\).
3 19 \(p[i][j]=0\) hoặc \(2\) với mọi \(i\ne j\), \(0 \le i,j \le n-1\).
4 35 \(0 \le p[i][j] \le 2\) với mọi \(0 \le i,j \le n-1\), và tồn tại ít nhất một phương án xây dựng thỏa mãn yêu cầu.
5 21 \(0 \le p[i][j] \le 2\) với mọi \(0 \le i,j \le n-1\).
6 4 Không có ràng buộc bổ sung.

Ví dụ

Ví dụ 1

Lời gọi
construct([[1, 1, 2, 2], [1, 1, 2, 2], [2, 2, 1, 2], [2, 2, 2, 1]])
Note

Điều này có nghĩa là phải có đúng một đường đi từ tháp \(0\) đến tháp \(1\). Với mọi cặp tháp khác \((x,y)\) thỏa mãn \(0 \le x<y \le 3\), phải có đúng hai đường đi từ tháp \(x\) đến tháp \(y\). Có thể đạt được điều này bằng \(4\) cầu nối các cặp tháp \((0,1)\), \((1,2)\), \((1,3)\)\((2,3)\).

Để báo cáo phương án này, hàm construct phải thực hiện lời gọi sau:

build([[0, 1, 0, 0], [1, 0, 1, 1], [0, 1, 0, 1], [0, 1, 1, 0]])

Sau đó, hàm phải trả về \(1\).

Trong trường hợp này, có nhiều phương án xây dựng thỏa mãn yêu cầu; tất cả đều được coi là đúng.

Ví dụ 2

Lời gọi
construct([[1, 0], [0, 1]])
Note

Điều này có nghĩa là không được có đường đi giữa hai tháp. Yêu cầu này chỉ có thể được thỏa mãn khi không có cầu nào.

Vì vậy, hàm construct phải thực hiện lời gọi sau:

build([[0, 0], [0, 0]])

Sau đó, hàm construct phải trả về \(1\).

Ví dụ 3

Lời gọi
construct([[1, 3], [3, 1]])
Note

Điều này có nghĩa là phải có đúng \(3\) đường đi từ tháp \(0\) đến tháp \(1\). Không thể thỏa mãn tập yêu cầu này. Vì vậy, hàm construct phải trả về \(0\) mà không gọi build lần nào.

Nguồn

IOI 2020, Ngày 1 — Connecting Supertrees (supertrees). Đề chính thức tiếng Anh và bản dịch tiếng Việt.

3. IOI 2020 - Carnival Tickets

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

Ringo đang tham dự một lễ hội ở Singapore. Cậu có một số vé dự thưởng trong túi và muốn sử dụng chúng tại quầy trò chơi có thưởng. Mỗi vé thuộc một trong \(n\) màu và có in một số nguyên không âm. Các số nguyên in trên những vé khác nhau có thể bằng nhau. Theo một luật lệ kỳ quặc của lễ hội, \(n\) được bảo đảm là số chẵn.

Ringo có \(m\) vé mỗi màu trong túi, tổng cộng \(n\cdot m\) vé. Vé \(j\) của màu \(i\) có in số nguyên \(x[i][j]\) (\(0 \le i \le n-1\)\(0 \le j \le m-1\)).

Trò chơi diễn ra trong \(k\) vòng, đánh số từ \(0\) đến \(k-1\). Mỗi vòng diễn ra theo thứ tự sau:

  1. Ringo chọn từ túi một tập gồm \(n\) vé, mỗi màu đúng một vé, rồi đưa tập vé này cho người quản trò.
  2. Người quản trò ghi lại các số nguyên \(a[0],a[1],\ldots,a[n-1]\) in trên các vé trong tập. Thứ tự của \(n\) số nguyên này không quan trọng.
  3. Người quản trò rút một thẻ đặc biệt từ hộp bốc thăm may mắn và ghi lại số nguyên \(b\) in trên thẻ đó.
  4. Người quản trò tính độ chênh lệch tuyệt đối giữa \(a[i]\)\(b\) với mỗi \(i\) từ \(0\) đến \(n-1\). Gọi \(S\) là tổng các độ chênh lệch tuyệt đối này.
  5. Trong vòng này, người quản trò trao cho Ringo phần thưởng có giá trị bằng \(S\).
  6. Các vé trong tập bị bỏ đi và không thể sử dụng trong các vòng sau.

Các vé còn lại trong túi Ringo sau \(k\) vòng chơi cũng bị bỏ đi.

Quan sát kỹ, Ringo nhận ra trò chơi đã bị gian lận! Thực ra có một máy in bên trong hộp bốc thăm may mắn. Trong mỗi vòng, người quản trò tìm một số nguyên \(b\) làm cho giá trị phần thưởng của vòng đó nhỏ nhất. Giá trị được chọn được in lên thẻ đặc biệt của vòng đó.

Biết tất cả những thông tin này, Ringo muốn phân bổ vé cho các vòng chơi. Cụ thể, cậu muốn chọn tập vé sử dụng trong mỗi vòng để tổng giá trị các phần thưởng là lớn nhất.

Chi tiết cài đặt

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

C++
long long find_maximum(int k, std::vector<std::vector<int>> x);
  • \(k\): số vòng chơi.
  • \(x\): mảng kích thước \(n\times m\) mô tả số nguyên in trên từng vé. Các vé của mỗi màu được sắp xếp theo thứ tự không giảm của số nguyên in trên vé.
  • Hàm này được gọi đúng một lần.
  • Hàm phải gọi allocate_tickets (mô tả bên dưới) đúng một lần, mô tả \(k\) tập vé, mỗi tập cho một vòng. Cách phân bổ phải làm tổng giá trị các phần thưởng lớn nhất.
  • Hàm phải trả về tổng giá trị phần thưởng lớn nhất.

Hàm allocate_tickets được định nghĩa như sau:

C++
void allocate_tickets(std::vector<std::vector<int>> s);
  • \(s\): mảng kích thước \(n\times m\). Giá trị \(s[i][j]\) phải bằng \(r\) nếu vé \(j\) của màu \(i\) được sử dụng trong tập vé của vòng \(r\), hoặc bằng \(-1\) nếu vé đó không được sử dụng.
  • Với mỗi \(0 \le i \le n-1\), trong các phần tử \(s[i][0],s[i][1],\ldots,s[i][m-1]\), mỗi giá trị \(0,1,2,\ldots,k-1\) phải xuất hiện đúng một lần; tất cả các phần tử còn lại phải bằng \(-1\).
  • Nếu có nhiều cách phân bổ đạt tổng giá trị phần thưởng lớn nhất, bạn có thể báo cáo bất kỳ cách nào.

Dữ liệu vào

Trình chấm mẫu đọc dữ liệu theo định dạng sau:

  • Dòng \(1\): n m k.
  • Dòng \(2+i\) (\(0 \le i \le n-1\)): x[i][0] x[i][1] ... x[i][m-1].

Dữ liệu ra

Trình chấm mẫu in câu trả lời theo định dạng sau:

  • Dòng \(1\): giá trị trả về của find_maximum.
  • Dòng \(2+i\) (\(0 \le i \le n-1\)): s[i][0] s[i][1] ... s[i][m-1].

Ràng buộc

  • \(2 \le n \le 1500\), trong đó \(n\) là số chẵn.
  • \(1 \le k \le m \le 1500\).
  • \(0 \le x[i][j] \le 10^9\) (\(0 \le i \le n-1\), \(0 \le j \le m-1\)).
  • \(x[i][j-1] \le x[i][j]\) (\(0 \le i \le n-1\), \(1 \le j \le m-1\)).

Phân nhóm

Nhóm Điểm Ràng buộc bổ sung
1 11 \(m=1\).
2 16 \(k=1\).
3 14 \(0 \le x[i][j] \le 1\) với mọi \(0 \le i \le n-1\)\(0 \le j \le m-1\).
4 14 \(k=m\).
5 12 \(n,m \le 80\).
6 23 \(n,m \le 300\).
7 10 Không có ràng buộc bổ sung.

Ví dụ

Ví dụ 1

Lời gọi
find_maximum(2, [[0, 2, 5], [1, 1, 3]])
Note

Điều này có nghĩa là:

  • \(k=2\) vòng chơi.
  • Các số nguyên in trên các vé màu \(0\) lần lượt là \(0\), \(2\)\(5\).
  • Các số nguyên in trên các vé màu \(1\) lần lượt là \(1\), \(1\)\(3\).

Một cách phân bổ đạt tổng giá trị phần thưởng lớn nhất là:

  • Ở vòng \(0\), Ringo chọn vé \(0\) của màu \(0\) (in số \(0\)) và vé \(2\) của màu \(1\) (in số \(3\)). Giá trị phần thưởng nhỏ nhất có thể trong vòng này là \(3\). Ví dụ, người quản trò có thể chọn \(b=1\):
\[ |1-0|+|1-3|=1+2=3. \]
    - Ở vòng $1$, Ringo chọn vé $2$ của màu $0$ (in số $5$) và vé $1$ của màu $1$ (in số $1$). Giá trị phần thưởng nhỏ nhất có thể trong vòng này là $4$. Ví dụ, người quản trò có thể chọn $b=3$:
\[ |3-1|+|3-5|=2+2=4. \]
    - Vì vậy, tổng giá trị phần thưởng là:
\[ 3+4=7. \]
    Để báo cáo cách phân bổ này, hàm `find_maximum` phải thực hiện lời gọi `allocate_tickets` sau:

    ```text
    allocate_tickets([[0, -1, 1], [-1, 1, 0]])
    ```

    Cuối cùng, hàm `find_maximum` phải trả về $7$.

Ví dụ 2

Lời gọi
find_maximum(1, [[5, 9], [1, 4], [3, 6], [2, 7]])
Note

Điều này có nghĩa là:

  • Chỉ có một vòng chơi.
  • Các số nguyên in trên các vé màu \(0\) lần lượt là \(5\)\(9\).
  • Các số nguyên in trên các vé màu \(1\) lần lượt là \(1\)\(4\).
  • Các số nguyên in trên các vé màu \(2\) lần lượt là \(3\)\(6\).
  • Các số nguyên in trên các vé màu \(3\) lần lượt là \(2\)\(7\).

Một cách phân bổ đạt tổng giá trị phần thưởng lớn nhất là:

  • Ở vòng \(0\), Ringo chọn vé \(1\) của màu \(0\) (in số \(9\)), vé \(0\) của màu \(1\) (in số \(1\)), vé \(0\) của màu \(2\) (in số \(3\)) và vé \(1\) của màu \(3\) (in số \(7\)).
  • Giá trị phần thưởng nhỏ nhất có thể trong vòng này là \(12\), khi người quản trò chọn \(b=3\):
\[ |3-9|+|3-1|+|3-3|+|3-7|=6+2+0+4=12. \]
    Để báo cáo phương án này, hàm `find_maximum` phải thực hiện lời gọi `allocate_tickets` sau:

    ```text
    allocate_tickets([[-1, 0], [0, -1], [0, -1], [-1, 0]])
    ```

    Cuối cùng, hàm `find_maximum` phải trả về $12$.

Nguồn

IOI 2020, Ngày 1 — Carnival Tickets (tickets). Đề chính thức tiếng Anh và bản dịch tiếng Việt.