Olympic Sinh Viên 2020 - Không chuyên - Dự trữ nước === [Link](https://oj.vnoi.info/problem/olp_kc20_game11) ----- ### Hướng dẫn Xét tại vị trí $i$, gọi mực nước dâng tới $b_i$ - Nếu như không tồn tại $j < i$ để $a_j > a_i$ hoặc không tồn tại $k > i$ để $a_k > a_i$, thì nước sẽ không đọng lại, nên $b_i$ - Ngược lại mực nước không được dâng quá $L = max(a_1, a_2, \dots, a_i)$ và cũng không quá $R = max(a_i, \dots, a_{n-1}, a_n)$ $a_i$ nên $b_i = min(L, R)$ Xét tại vị trí $i$, mình nâng cột lên vị trí $X$, hỏi phần nước còn lại là bao nhiêu - Phần nước ở ô $i$ là $water = b_i - a_i$ - Phần bị cô cạn là $drain = max(0, min(x, b[i]) - a[i])$ - Phần nước còn lại là $remain = water - drain$ Vì càng dâng cột lên cao, lượng nước còn lại giảm, nên hàm là hàm đơn điệu $\Rightarrow$ Chặt nhị phân ----- ### Code > **Time:** $O(n\ log\ (max(a) - min(a)))$ > **Space:** $O(n)$ > **Algo:** Binary Search, Implementation ```cpp= #include <algorithm> #include <iostream> #include <deque> using namespace std; typedef long long ll; const int LIM = 1e5 + 15; const int INF = 0x3f3f3f3f; const ll LINF = 0x3f3f3f3f3f3f3f3f; int n, M; int a[LIM]; int b[LIM]; bool check(int x) { int remains = 0; for (int i = 1; i <= n; ++i) { int water = b[i] - a[i]; int drain = max(0, min(x, b[i]) - a[i]); remains += water - drain; if (remains >= M) return true; } return false; } int main() { ios::sync_with_stdio(NULL); cin.tie(NULL); cin >> n >> M; for (int i = 1; i <= n; ++i) cin >> a[i]; for (int i = 1, pref = 0; i <= n; ++i) { if (pref < a[i]) pref = a[i]; b[i] = pref; } for (int i = n, suff = 0; i >= 1; --i) { if (suff < a[i]) suff = a[i]; b[i] = min(b[i], suff); } int res = -1; int mn = *min_element(a + 1, a + n + 1); int mx = *max_element(a + 1, a + n + 1); for (int l = mn, r = mx; l <= r; ) { int m = (l + r) >> 1; if (check(m)) { res = m; l = m + 1; } else { r = m - 1; } } cout << res; return 0; } ```