Codeforces 505E Mr. Kitayuta vs. Bamboos (貪心,二分答案,堆)
阿新 • • 發佈:2018-11-04
題意
有\(n\)根竹子,竹子\(i\)初始高度為\(h_i\),每天晚上會長高\(a_i\)。
每天白天,你可以選擇\(k\)根竹子(同一根竹子在同一個白天可以多次選擇),把他們的高度減少\(p\),若竹子當前高度\(-p\)後\(<0\),則竹子高度變為\(0\)。
最小化\(m\)天后最高的竹子的高度。
題解
首先最小化最大的...
這種問題,顯然可以用二分答案。
二分\(m\)天后最高的竹子的高度\(H\),然後問題就變成了判定性問題:是否存在一種方案,使得\(m\)天后竹子高度都\(\le H\)。
考慮怎麼解決這個判定性問題。
如果按照題意一天一天模擬,就需要考慮把竹子高度減\(p\)
所以我們嘗試倒著模擬這一過程。
即:竹子初始高度都設為\(H\),每根竹子每天會減少\(a_i\)的高度,然後你可以選擇\(k\)根竹子,把它們“拔高”\(p\)。問\(m\)天后竹子高度是否都\(\ge h_i\)。
此時你必須保證竹子減少\(a_i\)的高度後不會\(<0\)。
這樣就好做了。我們用一個堆維護 當前狀態下繼續減少高度而不“拔高”,第\(m\)天結束後竹子高度會\(<h_i\)的竹子 一直減少高度 多少天后的高度會\(<0\)。
(不理解這句話可以嘗試看程式碼理解)
每次取出最快\(<0\)的竹子,對它“拔高”即可。注意中間可能會出現無論怎麼“拔高”還是會\(<0\)
最後判斷堆是否為空即可,因為堆中維護的是\(m\)天后竹子高度會\(<h_i\)的竹子,所以堆空即代表所有竹子高度都\(\ge h_i\)。
時間複雜度\(O((n+mk)\log n\log mx)\),其中\(mx\)表示\(\max\limits_{1\le i\le n} h_i+a_im\)(二分的上界)。
程式碼
#include <cstdio> #include <cctype> #include <cstring> #include <algorithm> int read(){ register int x = 0; register char f = 1, ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = 0; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0'); return f ? x : -x; } int n, m, k, c[100005]; long long p, a[100005], h[100005], l = 0, r, mid, ans; struct node{ int day, id; // 表示當前狀態下(二分的高度+c[id]*p)day+1天后竹子id的高度會<0 bool operator < (const node &b) const { // 預設大根堆,所以過載<時寫的是> return day > b.day; } }; struct Heap{ // 用algorithm中的堆相關的演算法封裝實現。 node h[200005]; int sz; void clear(){ sz = 0; } bool empty(){ return !sz; } void push(node x){ h[++sz] = x, std :: push_heap(h + 1, h + 1 + sz); } node pop(){ return std :: pop_heap(h + 1, h + 1 + sz), h[sz--]; } node top(){ return h[1]; } }H; bool check(long long x){ H.clear(), memset(c, 0, sizeof c); // c[i]表示竹子i被“拔高”了幾次 for (register int i = 1; i <= n; ++i) if (x - a[i] * m < h[i]) H.push((node){x / a[i], i}); // 初始堆的狀態 for (register int i = 1; !H.empty() && i <= m; ++i) // i表示倒著的第幾天 for (register int j = 1; !H.empty() && j <= k; ++j){ // 拔高k根竹子 node u = H.pop(); if (u.day < i) return 0; // 無論怎麼“拔高”都不能滿足條件 ++c[u.id]; // “拔高” if (x + c[u.id] * p - a[u.id] * m < h[u.id]) // 還是不滿足條件,就插入堆中 H.push((node){(x + c[u.id] * p) / a[u.id], u.id}); } return H.empty(); } int main(){ n = read(), m = read(), k = read(), p = read(); for (register int i = 1; i <= n; ++i) h[i] = read(), a[i] = read(), r = std :: max(r, h[i] + a[i] * m); // 二分上界 while (l <= r) check(mid = l + r >> 1) ? ans = mid, r = mid - 1 : l = mid + 1; printf("%lld", ans); }