Hướng dẫn giải của Bedao Mini Contest 21 - Cắt dây
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.
Tác giả:
Subtask ~1~: Ta luôn chia đôi sợi dây dài nhất.
Subtask ~2~: Giả sử ~x~ là giá trị làm tròn lên của đáp án. Ta đảm bảo được rằng có thể cắt các sợi dây để cho ra giá trị ~x~, ~x + 1~, ~x + 2~, ... Nhưng không thể cắt các sợi dây để cho ra giá trị nhỏ hơn hoặc bằng ~x - 1~. Vậy nên ta không cần quan tâm đến số thực, ta chỉ cần quan tâm giá trị nguyên ~x~ nhỏ nhất mà ta có thể cắt các sợi dây ra, sau ~k~ phép cắt dây, sao cho sợi dây có độ dài lớn nhất ~\le x~.
Cách làm ở subtask này là duyệt qua mọi giá trị ~x~, kiểm tra xem có thể cắt sao cho độ dài lớn nhất ~\le x~ hay không. Với mỗi sợi dây, ta sẽ tính số phép cắt cần ít nhất là ~\lceil \frac{A_i}{x} \rceil~. Nếu tổng các phép cắt cần thiết lớn hơn ~k~ thì không thoả mãn. Ngược lại ta sẽ luôn cắt được.
Subtask ~3~: Sử dụng phương pháp tìm kiếm nhị phân.
Code mẫu
#include<bits/stdc++.h> #define int long long #define pb push_back #define fi first #define se second using namespace std; using ll = long long; using ld = long double; using ull = unsigned long long; mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); const int maxN = 1e6 + 5; const int mod = 1e9 + 7; const ll oo = 1e18; int n, k; int a[maxN]; void ReadInput() { cin >> n >> k; for(int i=1; i<=n; i++) cin >> a[i]; } bool chk(int val) { int cnt = 0; for(int i=1; i<=n; i++) { cnt += (a[i] - 1) / val; } return cnt <= k; } void Solve() { int low = 1, high = oo, mid; while(low <= high) { mid = (low + high) / 2; if(chk(mid)) high = mid - 1; else low = mid + 1; } cout << low; } int32_t main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); ReadInput(); Solve(); }
Bình luận