題解-CF436E Cardboard Box
阿新 • • 發佈:2020-11-13
題面
\(n\) 個關卡,對每個關卡可以花 \(a_i\) 時間得到 \(1\) 顆星,或花 \(b_i\) 時間得到 \(2\) 顆星,或不玩。問獲得 \(m\) 顆星最少需要多少時間。
資料範圍:\(1\le n\le 3\cdot 10^5\),\(1\le m\le 2n\),\(a_i<b_i\)。
題解
蒟蒻做這題時已經知道是反悔貪心了,但是做了 \(4\) 個小時做不出來。於是去看題解,感覺這個 \(5\) 堆做法太非人類了,於是去看
CF
題解和程式碼後總結出了蒟蒻的不用反悔貪心的做法和題解。
如果不反悔貪心,那麼每一步必須保證沒有後效性地最優。
很明顯如果只有 \(a_i\),排序可以滿足這個條件。
這題最樸素的錯誤做法是用一個小頂堆,剛開始把所有 \(a_i\) 丟進去,然後 \(m\) 次每次取堆頂,如果選了 \(a_i\) 把 \(b_i-a_i\) 也丟進去。
如果 \(a_i\) 都很大,\(b_i-a_i\) 都很小這樣顯然是虧的。
假如某個 \(i\) 打出 \(2\) 星是當前最優,如果 \(a_i\) 已經取了按照上面的辦法肯定沒有問題;但是如果 \(a_i\) 都沒取,\(a_i\) 不一定是單箇中的最優。
所以這篇題解最重要的騷操作就來了:選之前權衡,選兩個的最優還是一個的最優:哪個更優?
可以用一個 vis
陣列記錄打了那些關卡,每次把打了的從堆中扔掉,這樣就可以假定堆中的都是沒有打過的了。
程式碼
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef double db; #define x first #define y second #define bg begin() #define ed end() #define pb push_back #define mp make_pair #define sz(a) int((a).size()) #define R(i,n) for(int i(0);i<(n);++i) #define L(i,n) for(int i((n)-1);~i;--i) const int iinf=0x3f3f3f3f; const ll linf=0x3f3f3f3f3f3f3f3f; //Data const int N=3e5,sN=N<<1; int n,m,star[N]; ll a[sN],ns; bool vis[sN]; priority_queue<pair<ll,int>> o,t; void ref(priority_queue<pair<ll,int>> &q){ while(sz(q)&&vis[q.top().y]) q.pop(); } //Main int main(){ ios::sync_with_stdio(0); cin.tie(0),cout.tie(0); cin>>n>>m; R(i,n) cin>>a[i]>>a[i+n],a[i+n]-=a[i], o.push(mp(-a[i],i)),t.push(mp(-a[i]-a[i+n],i)); while(m--){ ref(o),ref(t); int i=o.top().y; o.pop(),ref(o); if(m&&sz(t)&&a[i]-o.top().x>=-t.top().x) o.push(mp(-a[i],i)),i=t.top().y,t.pop(); i<n&&(o.push(mp(-a[i+n],i+n)),true); ns+=a[i],star[i%n]++,vis[i]=true; } cout<<ns<<'\n'; R(i,n) cout<<star[i],i==n-1&&(cout<<'\n'); return 0; } /* 可以考慮不反悔:權衡一個兩個哪個優! */
祝大家學習愉快!