IOI 2015 - Ngày 2

Bộ đề bài

# Bài tập Điểm Thời gian: Giới hạn bộ nhớ
1 IOI 2015 - Horses 100 (p) 1.5s 1G
2 IOI 2015 - Sorting 100 (p) 1.0s 1G
3 IOI 2015 - Towns 100 (p) 1.0s 1G

1. IOI 2015 - Horses

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

Mansur thích nuôi ngựa như tổ tiên của mình. Hiện nay anh có đàn ngựa lớn nhất Kazakhstan, nhưng trước đây không phải lúc nào cũng vậy. Cách đây \(N\) năm, Mansur chỉ là một dzhigit (tiếng Kazakhstan nghĩa là chàng trai trẻ) và có đúng một con ngựa. Anh mơ ước kiếm được nhiều tiền để trở thành một bai (người rất giàu có).

Đánh số các năm từ \(0\) đến \(N-1\) theo thứ tự thời gian, nên năm \(N-1\) là năm gần đây nhất. Thời tiết mỗi năm ảnh hưởng đến sự tăng trưởng của đàn ngựa. Với năm \(i\), Mansur nhớ hệ số tăng trưởng nguyên dương \(X[i]\): nếu bắt đầu năm với \(h\) con ngựa thì cuối năm, trước khi bán, sẽ có \(h \cdot X[i]\) con.

Ngựa chỉ được bán vào cuối năm. Với mỗi năm \(i\), Mansur nhớ số nguyên dương \(Y[i]\), là giá bán một con ngựa vào cuối năm đó. Sau mỗi năm, anh có thể bán số ngựa tùy ý trong đàn, mỗi con cùng giá \(Y[i]\).

Mansur muốn biết số tiền lớn nhất anh có thể có hiện nay nếu đã chọn thời điểm bán ngựa tốt nhất trong \(N\) năm. Bạn vinh dự là khách dự toi (ngày lễ) của Mansur và được anh nhờ trả lời câu hỏi này.

Trong buổi tối, trí nhớ của Mansur dần tốt hơn nên anh đưa ra \(M\) lần cập nhật. Mỗi lần thay đổi một giá trị \(X[i]\) hoặc \(Y[i]\), rồi anh lại hỏi số tiền lớn nhất có thể thu được. Các cập nhật có tính tích lũy: mỗi câu trả lời phải xét mọi cập nhật trước đó. Một giá trị \(X[i]\) hay \(Y[i]\) có thể được cập nhật nhiều lần.

Kết quả có thể rất lớn. Bạn chỉ cần trả về phần dư khi chia kết quả tối ưu cho \(10^9+7\).

Ví dụ

\(N=3\) năm, với dữ liệu ban đầu:

Năm 0 1 2
\(X\) 2 1 3
\(Y\) 3 4 1

Mansur thu được nhiều tiền nhất bằng cách bán cả hai con ngựa vào cuối năm \(1\):

  • Ban đầu có \(1\) con ngựa.
  • Sau năm \(0\)\(1 \cdot 2=2\) con.
  • Sau năm \(1\)\(2 \cdot 1=2\) con.
  • Bán cả hai con, thu \(2 \cdot 4=8\).

Sau đó có \(M=1\) cập nhật: đổi \(Y[1]\) thành \(2\). Dữ liệu trở thành:

Năm 0 1 2
\(X\) 2 1 3
\(Y\) 3 2 1

Một phương án tối ưu lúc này là bán một con sau năm \(0\), rồi ba con sau năm \(2\):

  • Ban đầu có \(1\) con ngựa.
  • Sau năm \(0\)\(1 \cdot 2=2\) con.
  • Bán một con, thu \(3\), còn một con.
  • Sau năm \(1\)\(1 \cdot 1=1\) con.
  • Sau năm \(2\)\(1 \cdot 3=3\) con.
  • Bán ba con, thu \(3 \cdot 1=3\). Tổng tiền là \(3+3=6\).

Chi tiết cài đặt

Cho \(N\), \(X\), \(Y\) và dãy cập nhật. Trước lần cập nhật đầu tiên và sau mỗi lần cập nhật, hãy tính số tiền lớn nhất Mansur có thể thu được, lấy phần dư modulo \(10^9+7\).

Trong C hoặc C++, cài đặt các hàm trong header dùng chung horses.h:

C++
int init(int N, int X[], int Y[]);
int updateX(int pos, int val);
int updateY(int pos, int val);

Trong Java, cài đặt các phương thức sau trong lớp horses:

Java
public int init(int N, int[] X, int[] Y)
public int updateX(int pos, int val)
public int updateY(int pos, int val)

Hàm init(N, X, Y) được gọi đầu tiên và đúng một lần:

  • N: số năm.
  • X: mảng có \(N\) phần tử; X[i] là hệ số tăng trưởng năm \(i\), với \(0 \le i \le N-1\).
  • Y: mảng có \(N\) phần tử; Y[i] là giá bán một con ngựa sau năm \(i\), với \(0 \le i \le N-1\).
  • Hai mảng chứa dữ liệu ban đầu, trước mọi cập nhật. Sau khi init kết thúc, XY vẫn hợp lệ; bạn có thể thay đổi nội dung của chúng nếu muốn.
  • Hàm trả về số tiền lớn nhất có thể thu được với dữ liệu ban đầu, modulo \(10^9+7\).

Hàm updateX(pos, val) thay đổi X[pos] thành val; hàm updateY(pos, val) thay đổi Y[pos] thành val. Trong cả hai hàm, \(0 \le \texttt{pos} \le N-1\). Mỗi hàm trả về số tiền lớn nhất có thể thu được sau cập nhật đó, modulo \(10^9+7\).

Mọi giá trị ban đầu và sau cập nhật của \(X[i]\), \(Y[i]\) đều thuộc đoạn \([1,10^9]\). Sau init, chương trình chấm gọi updateXupdateY tổng cộng \(M\) lần.

Chỉ nộp phần cài đặt hàm, không viết main. C và C++ đều dùng #include "horses.h"; LQDOJ cung cấp cùng tên header cho cả hai ngôn ngữ. Java dùng lớp horses, không viết phương thức main.

Phân nhóm

Mỗi subtask được trọn điểm nếu tất cả test của subtask đều đúng, nếu không được \(0\) điểm. Test mẫu là pretest \(0\) điểm.

Subtask Điểm \(N\) \(M\) Ràng buộc bổ sung
1 17 \(1 \le N \le 10\) \(M=0\) \(X[i],Y[i] \le 10\)\(X[0] \cdot X[1] \cdots X[N-1] \le 1000\).
2 17 \(1 \le N \le 1000\) \(0 \le M \le 1000\) Không có.
3 20 \(1 \le N \le 500000\) \(0 \le M \le 100000\) \(X[i] \ge 2\) trong initval \(\ge 2\) trong updateX.
4 23 \(1 \le N \le 500000\) \(0 \le M \le 10000\) Không có.
5 23 \(1 \le N \le 500000\) \(0 \le M \le 100000\) Không có.

Chương trình chấm mẫu

Chương trình chấm mẫu đọc tệp horses.in:

  • Dòng \(1\): N.
  • Dòng \(2\): X[0] ... X[N-1].
  • Dòng \(3\): Y[0] ... Y[N-1].
  • Dòng \(4\): M.
  • Các dòng \(5,\ldots,M+4\): type pos val; type=1 tương ứng với updateX, type=2 tương ứng với updateY.

Chương trình in giá trị trả về của init, tiếp theo là giá trị trả về của từng lần gọi updateX hoặc updateY, mỗi giá trị trên một dòng. Grader trên LQDOJ dùng đầu vào/đầu ra chuẩn thay cho tệp, giữ nguyên giao diện hàm.

Dữ liệu vào:

3
2 1 3
3 4 1
1
2 1 2

Kết quả:

8
6

2. IOI 2015 - Sorting

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

Aizhan có dãy \(N\) số nguyên \(S[0],S[1],\ldots,S[N-1]\), gồm các số từ \(0\) đến \(N-1\), mỗi số xuất hiện đúng một lần. Cô muốn sắp xếp dãy tăng dần bằng cách đổi chỗ một số cặp phần tử. Bạn của cô là Ermek cũng sẽ đổi chỗ một số cặp phần tử, nhưng không nhất thiết giúp việc sắp xếp dễ hơn.

Hai người thay đổi dãy qua các vòng. Trong mỗi vòng, Ermek đổi chỗ trước, sau đó Aizhan thực hiện một phép đổi chỗ. Mỗi người chọn hai chỉ số hợp lệ rồi đổi chỗ các phần tử ở hai chỉ số đó. Hai chỉ số được phép bằng nhau; khi ấy một phần tử đổi chỗ với chính nó và dãy không thay đổi.

Aizhan biết Ermek không thực sự quan tâm đến việc sắp xếp \(S\) và biết chính xác những chỉ số mà Ermek sẽ chọn. Ermek dự định tham gia \(M\) vòng, đánh số từ \(0\) đến \(M-1\). Tại vòng \(i\), Ermek chọn hai chỉ số \(X[i]\)\(Y[i]\).

Trước mỗi vòng, nếu thấy dãy đã tăng dần, Aizhan sẽ kết thúc toàn bộ quá trình. Cho dãy ban đầu và các chỉ số Ermek sẽ chọn, hãy tìm một dãy phép đổi chỗ mà Aizhan có thể dùng để sắp xếp \(S\). Trong một số subtask, dãy phép đổi chỗ phải ngắn nhất có thể. Đảm bảo có thể sắp xếp trong không quá \(M\) vòng.

Nếu dãy đã tăng dần ngay sau phép đổi chỗ của Ermek, Aizhan có thể đổi chỗ hai chỉ số bằng nhau, chẳng hạn \((0,0)\). Dãy vẫn tăng dần sau khi kết thúc vòng và Aizhan đạt mục tiêu. Nếu dãy ban đầu đã tăng dần, số vòng ít nhất là \(0\).

Ví dụ 1

Dãy ban đầu là \(S=(4,3,2,1,0)\) và Ermek sẵn sàng thực hiện \(M=6\) phép đổi chỗ. Hai dãy chỉ số là \(X=(0,1,2,3,0,1)\)\(Y=(1,2,3,4,1,2)\); các cặp của Ermek lần lượt là \((0,1)\), \((1,2)\), \((2,3)\), \((3,4)\), \((0,1)\), \((1,2)\).

Aizhan có thể đưa dãy về \((0,1,2,3,4)\) trong ba vòng bằng cách chọn lần lượt \((0,4)\), \((1,3)\), \((3,4)\):

Vòng Người thực hiện Cặp chỉ số Dãy sau thao tác
Ban đầu \(4,3,2,1,0\)
0 Ermek \((0,1)\) \(3,4,2,1,0\)
0 Aizhan \((0,4)\) \(0,4,2,1,3\)
1 Ermek \((1,2)\) \(0,2,4,1,3\)
1 Aizhan \((1,3)\) \(0,1,4,2,3\)
2 Ermek \((2,3)\) \(0,1,2,4,3\)
2 Aizhan \((3,4)\) \(0,1,2,3,4\)

Ví dụ 2

Dãy ban đầu là \(S=(3,0,4,2,1)\) và Ermek sẵn sàng thực hiện \(M=5\) phép đổi chỗ. Các cặp chỉ số của Ermek lần lượt là \((1,1)\), \((4,0)\), \((2,3)\), \((1,4)\), \((0,4)\).

Aizhan có thể sắp xếp trong ba vòng, chẳng hạn chọn lần lượt \((1,4)\), \((4,2)\), \((2,2)\):

Vòng Người thực hiện Cặp chỉ số Dãy sau thao tác
Ban đầu \(3,0,4,2,1\)
0 Ermek \((1,1)\) \(3,0,4,2,1\)
0 Aizhan \((1,4)\) \(3,1,4,2,0\)
1 Ermek \((4,0)\) \(0,1,4,2,3\)
1 Aizhan \((4,2)\) \(0,1,3,2,4\)
2 Ermek \((2,3)\) \(0,1,2,3,4\)
2 Aizhan \((2,2)\) \(0,1,2,3,4\)

Chi tiết cài đặt

Cho \(S\), \(M\), \(X\), \(Y\), hãy tìm dãy phép đổi chỗ của Aizhan. Ở subtasks \(5\)\(6\), phải tìm dãy ngắn nhất.

Trong C hoặc C++, cài đặt hàm trong header dùng chung sorting.h:

C++
int findSwapPairs(int N, int S[], int M, int X[], int Y[], int P[], int Q[]);

Trong Java, cài đặt phương thức sau trong lớp sorting:

Java
public int findSwapPairs(int N, int[] S, int M, int[] X, int[] Y, int[] P, int[] Q)

Hàm được chương trình chấm gọi đúng một lần:

  • N: độ dài dãy \(S\).
  • S: mảng số nguyên chứa dãy ban đầu.
  • M: số phép đổi chỗ Ermek dự định thực hiện.
  • X, Y: hai mảng số nguyên dài \(M\). Tại vòng \(i\), với \(0 \le i \le M-1\), Ermek đổi chỗ các phần tử tại X[i], Y[i].
  • P, Q: hai mảng số nguyên, mỗi mảng đã được cấp phát \(M\) phần tử, dùng để trả về một dãy phép đổi chỗ hợp lệ của Aizhan. Gọi \(R\) là độ dài dãy tìm được. Với \(0 \le i \le R-1\), ghi hai chỉ số Aizhan chọn ở vòng \(i\) vào P[i], Q[i].
  • Hàm trả về \(R\), với \(0 \le R \le M\). Các chỉ số phải thuộc đoạn \([0,N-1]\); hai chỉ số của một phép đổi chỗ được phép bằng nhau.

Chỉ nộp phần cài đặt hàm, không viết main. C và C++ đều dùng #include "sorting.h"; LQDOJ cung cấp cùng tên header cho cả hai ngôn ngữ. Java dùng lớp sorting, không viết phương thức main. Có thể trả về bất kỳ dãy phép đổi chỗ thỏa yêu cầu; bộ chấm kiểm tra tác dụng của các phép đổi chỗ, không so sánh nguyên văn với một đáp án mẫu.

Phân nhóm

Luôn tồn tại lời giải cần không quá \(M\) vòng. Mỗi subtask được trọn điểm nếu tất cả test của subtask đều đúng, nếu không được \(0\) điểm. Các mẫu là pretest \(0\) điểm.

Subtask Điểm \(N\) \(M\) Ràng buộc bổ sung trên \(X\), \(Y\) Yêu cầu đối với \(R\)
1 8 \(1 \le N \le 5\) \(M=N^2\) \(X[i]=Y[i]=0\) với mọi \(i\). \(R \le M\).
2 12 \(1 \le N \le 100\) \(M=30N\) \(X[i]=Y[i]=0\) với mọi \(i\). \(R \le M\).
3 16 \(1 \le N \le 100\) \(M=30N\) \(X[i]=0\), \(Y[i]=1\) với mọi \(i\). \(R \le M\).
4 18 \(1 \le N \le 500\) \(M=30N\) Không có. \(R \le M\).
5 20 \(6 \le N \le 2000\) \(M=3N\) Không có. Nhỏ nhất có thể.
6 26 \(6 \le N \le 200000\) \(M=3N\) Không có. Nhỏ nhất có thể.

Chương trình chấm mẫu

Chương trình chấm mẫu đọc tệp sorting.in:

  • Dòng \(1\): N.
  • Dòng \(2\): S[0] ... S[N-1].
  • Dòng \(3\): M.
  • Các dòng \(4,\ldots,M+3\): X[i] Y[i], theo thứ tự \(i=0,\ldots,M-1\).

Kết quả được in theo định dạng:

  • Dòng \(1\): giá trị \(R\) trả về bởi findSwapPairs.
  • Dòng \(2+i\), với \(0 \le i \le R-1\): P[i] Q[i].

Grader trên LQDOJ dùng đầu vào/đầu ra chuẩn thay cho tệp, giữ nguyên giao diện hàm.

Dữ liệu cho ví dụ 1:

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

Một kết quả hợp lệ:

3
0 4
1 3
3 4

Dữ liệu cho ví dụ 2:

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

Một kết quả hợp lệ:

3
1 4
4 2
2 2

3. IOI 2015 - Towns

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

Kazakhstan có \(N\) thị trấn nhỏ, đánh số từ \(0\) đến \(N-1\), và một số lượng chưa biết các thành phố lớn. Các thị trấn nhỏ và thành phố lớn được gọi chung là các đô thị.

Tất cả đô thị được nối bởi một mạng đường cao tốc hai chiều duy nhất. Mỗi đường nối hai đô thị khác nhau; giữa một cặp đô thị có nhiều nhất một đường nối trực tiếp. Với mỗi cặp đô thị \(a\), \(b\), có duy nhất một cách đi từ \(a\) đến \(b\) bằng các đường cao tốc mà không sử dụng đường nào quá một lần.

Mỗi thị trấn nhỏ nối trực tiếp với đúng một đô thị khác; mỗi thành phố lớn nối trực tiếp với ít nhất ba đô thị.

Hình sau minh họa mạng gồm \(11\) thị trấn nhỏ và \(7\) thành phố lớn. Thị trấn được vẽ bằng hình tròn, ghi nhãn số nguyên; thành phố được vẽ bằng hình vuông, ghi nhãn chữ cái.

Mỗi đường cao tốc có độ dài nguyên dương. Khoảng cách giữa hai đô thị là tổng độ dài nhỏ nhất của các đường cần đi qua để từ đô thị này tới đô thị kia.

Với thành phố lớn \(C\), gọi \(r(C)\) là khoảng cách từ \(C\) đến thị trấn nhỏ xa nó nhất. Thành phố lớn \(C\) là một hub nếu \(r(C)\) nhỏ nhất trong số tất cả các thành phố lớn. Ký hiệu \(R\) là khoảng cách từ một hub đến thị trấn nhỏ xa nó nhất; như vậy \(R\) là giá trị nhỏ nhất trong tất cả các \(r(C)\).

Trong hình, thị trấn xa thành phố \(a\) nhất là thị trấn \(8\), với

\[ r(a)=1+4+12=17. \]

Thành phố \(g\) cũng có \(r(g)=17\); một thị trấn xa \(g\) nhất là thị trấn \(6\). Hub duy nhất là thành phố \(f\), với \(r(f)=16\), nên \(R=16\).

Loại bỏ một hub sẽ chia mạng thành nhiều thành phần liên thông. Hub là cân bằng nếu mỗi thành phần chứa không quá \(\lfloor N/2\rfloor\) thị trấn nhỏ. Chỉ đếm thị trấn nhỏ, không đếm thành phố lớn. Ký hiệu \(\lfloor x\rfloor\) là số nguyên lớn nhất không vượt quá \(x\).

Trong ví dụ, loại bỏ \(f\) tạo thành bốn thành phần có các tập thị trấn \(\{0,1,10\}\), \(\{2,3\}\), \(\{4,5,6,7\}\)\(\{8,9\}\). Không thành phần nào có quá \(\lfloor 11/2\rfloor=5\) thị trấn nhỏ, nên \(f\) là hub cân bằng.

Chi tiết cài đặt

Ban đầu bạn chỉ biết \(N\), số thị trấn nhỏ; không biết số thành phố lớn hay cách bố trí đường cao tốc. Bạn chỉ có thể lấy thêm thông tin bằng cách hỏi khoảng cách giữa hai thị trấn nhỏ.

Cần xác định \(R\) trong mọi subtask. Với subtasks \(3\) đến \(6\), còn phải xác định có tồn tại hub cân bằng hay không.

Trong C hoặc C++, cài đặt hàm sau trong header dùng chung towns.h:

C++
int hubDistance(int N, int sub);

Chương trình chấm cung cấp hàm:

C++
int getDistance(int i, int j);

Trong Java, cài đặt phương thức trong lớp towns:

Java
public int hubDistance(int N, int sub)

Gọi phương thức do chương trình chấm cung cấp qua grader.lib.getDistance(i, j):

Java
public static int getDistance(int i, int j)

Trong một lần chạy, chương trình chấm xét nhiều test, tối đa \(40\) test. Với mỗi test, hubDistance được gọi đúng một lần. Phải khởi tạo lại mọi biến cần thiết ở mỗi lần gọi.

  • N: số thị trấn nhỏ.
  • sub: số hiệu subtask, như bảng bên dưới.
  • Nếu sub bằng \(1\) hoặc \(2\), có thể trả về \(R\) hoặc \(-R\).
  • Nếu sub lớn hơn \(2\), phải trả về \(R\) khi có hub cân bằng, và \(-R\) khi không có.
  • getDistance(i, j) trả về khoảng cách giữa hai thị trấn nhỏ \(i\)\(j\). Nếu \(i=j\) thì trả về \(0\); nếu đối số không hợp lệ cũng trả về \(0\). Mỗi lần gọi vẫn được tính vào giới hạn truy vấn.

Chỉ nộp phần cài đặt hàm, không viết main. C và C++ đều dùng #include "towns.h"; LQDOJ cung cấp cùng tên header cho cả hai ngôn ngữ. Java dùng lớp towns, không viết phương thức main. Không đọc trực tiếp ma trận khoảng cách bí mật; chỉ dùng getDistance để lấy thông tin.

Phân nhóm

Trong mỗi test:

  • \(6 \le N \le 110\).
  • Khoảng cách giữa hai thị trấn nhỏ khác nhau thuộc đoạn \([1,1000000]\).

Số lần gọi getDistance bị giới hạn theo subtask. Nếu vượt giới hạn, chương trình bị dừng và test được xem là trả lời sai. Mỗi subtask được trọn điểm nếu tất cả test của subtask đều đúng, nếu không được \(0\) điểm. Test mẫu là pretest \(0\) điểm.

Subtask Điểm Số truy vấn tối đa Phải xác định hub cân bằng Ràng buộc bổ sung
1 13 \(N(N-1)/2\) Không Không có.
2 12 \(\lceil 7N/2\rceil\) Không Không có.
3 13 \(N(N-1)/2\) Không có.
4 10 \(\lceil 7N/2\rceil\) Mỗi thành phố lớn nối trực tiếp với đúng ba đô thị.
5 13 \(5N\) Không có.
6 39 \(\lceil 7N/2\rceil\) Không có.

Ký hiệu \(\lceil x\rceil\) là số nguyên nhỏ nhất lớn hơn hoặc bằng \(x\).

Chương trình chấm mẫu

Số hiệu subtask là một phần của dữ liệu vào; chương trình chấm mẫu thay đổi hành vi theo số hiệu đó. Chương trình đọc tệp towns.in theo định dạng:

  • Dòng \(1\): số hiệu subtask và số test.
  • Dòng \(2\): \(N_1\), số thị trấn nhỏ của test đầu tiên.
  • \(N_1\) dòng tiếp theo: số thứ \(j\) (\(1 \le j \le N_1\)) trên dòng thứ \(i\) (\(1 \le i \le N_1\)) là khoảng cách giữa thị trấn \(i-1\)\(j-1\).
  • Các test tiếp theo có cùng định dạng như test đầu tiên.

Đề chính thức mô tả chương trình chấm mẫu in giá trị trả về của hubDistance và số truy vấn trên hai dòng riêng biệt. Mã grader thực tế trong gói chính thức chỉ in giá trị trả về; grader LQDOJ giữ cách in này, kiểm soát số truy vấn bên trong và chuyển phần đọc/ghi sang đầu vào/đầu ra chuẩn.

Dữ liệu tương ứng với hình trên:

1 1
11
0 17 18 20 17 12 20 16 23 20 11
17 0 23 25 22 17 25 21 28 25 16
18 23 0 12 21 16 24 20 27 24 17
20 25 12 0 23 18 26 22 29 26 19
17 22 21 23 0 9 21 17 26 23 16
12 17 16 18 9 0 16 12 21 18 11
20 25 24 26 21 16 0 10 29 26 19
16 21 20 22 17 12 10 0 25 22 15
23 28 27 29 26 21 29 25 0 21 22
20 25 24 26 23 18 26 22 21 0 19
11 16 17 19 16 11 19 15 22 19 0

Một kết quả hợp lệ của grader trong gói đính kèm và trên LQDOJ:

16

Định dạng ma trận này khác với việc liệt kê các đường cao tốc. Bạn được phép sửa chương trình chấm mẫu để dùng định dạng đầu vào khác khi thử chương trình của mình.