Hướng dẫn giải của [VTT Q4 Contest 06] Bài 4: Huấn luyện [TRAINING]
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Hướng dẫn giải
Mỗi vận động viên nên tập các bài từ dễ đến khó: sắp xếp độ khó tăng dần, bài nào làm được thì làm (làm bài dễ trước luôn có lợi vì kỹ năng chỉ tăng). Gặp bài đầu tiên không làm được thì các bài sau còn khó hơn nên dừng.
Sub 1: Mô phỏng tham lam — ~O(n \cdot m)~, dùng khi ~n, m \le 1000~
Sắp xếp ~b~ một lần, với mỗi vận động viên duyệt lần lượt các bài và cộng vào kỹ năng khi làm được. Nếu chỉ nộp sub này: đúng 12/20 test (8 test lớn bị quá thời gian).
Sub 2: Tổng tiền tố và chặt nhị phân — ~O(n \log m \log C)~
Gọi ~pre~ là tổng tiền tố của ~b~ đã sắp xếp. Với kỹ năng hiện tại ~s~, chặt nhị phân tìm số bài ~k~ có độ khó ~\le s~, khi đó kỹ năng mới là ~a_i + pre_k~. Lặp lại đến khi ~k~ không tăng. Mỗi lần ~k~ tăng thì bài mới thêm vào có độ khó lớn hơn ~s~ cũ nên kỹ năng ít nhất gấp đôi, vì vậy chỉ cần cỡ ~\log C~ vòng. Nếu chỉ nộp sub này: đúng 20/20 test. Chú ý dùng long long vì tổng lên tới ~5 \cdot 10^{14}~.
Chương trình hoàn chỉnh
Main đọc dữ liệu, sắp xếp ~b~, tính ~pre~; nếu ~n, m \le 1000~ thì gọi sub1, ngược lại sub2. Khi đi thi nên viết như vậy: sub sau có sai hoặc chưa kịp làm thì vẫn ăn điểm sub trước.
#include <bits/stdc++.h>
using namespace std;
int n, m;
long long a[500005], b[500005], pre[500005];
// Sub 1: với mỗi vận động viên, duyệt các bài đã sắp xếp tăng dần, làm được bài nào thì làm. ~O(n * m)~
void sub1() {
for (int i = 1; i <= n; i++) {
long long s = a[i];
for (int j = 1; j <= m; j++) {
if (b[j] <= s) s += b[j];
else break; // bài sau còn khó hơn nên không làm được nữa
}
cout << s << " ";
}
cout << "\n";
}
// Sub 2: sắp xếp b, có tổng tiền tố pre. Với kỹ năng s, ta làm được mọi bài có độ khó <= s (chặt nhị phân),
// kỹ năng mới = a + pre[k]; lặp đến khi k không tăng. Mỗi lần k tăng thì kỹ năng ít nhất gấp đôi
// nên chỉ ~O(\log C)~ vòng. ~O(n \log m \log C)~
void sub2() {
for (int i = 1; i <= n; i++) {
long long s = a[i];
int k = 0;
while (true) {
int nk = upper_bound(b + 1, b + m + 1, s) - (b + 1);
if (nk == k) break;
k = nk;
s = a[i] + pre[k];
}
cout << s << " ";
}
cout << "\n";
}
int main() {
freopen("TRAINING.INP", "r", stdin);
freopen("TRAINING.OUT", "w", stdout);
ios::sync_with_stdio(false); cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int j = 1; j <= m; j++) cin >> b[j];
sort(b + 1, b + m + 1);
for (int j = 1; j <= m; j++) pre[j] = pre[j - 1] + b[j];
if (n <= 1000 && m <= 1000) sub1();
else sub2();
}
Bình luận