Loading

题解:CF436E Cardboard Box

做一道 IOI 的题时需要用这个,以前没写这个,现在补一下,还是很典的,加深了对反悔贪心的理解。

CF436E

首先很明显,如果要用反悔贪心做这题,大体思路就是看每选一个星星最少可以用多少时间。但是注意到如果你的决策只有 \(0 \to 1\)\(1 \to 2\) 的话,肯定是不对的,可能会有拿第一颗星花很久,但是第二颗极快的情况。于是我们要在这个决策集里再加上所有可以反悔,并且使总星数加 \(1\) 的决策。所以总的决策集就是:

  • 选一个下标 \(0 \to 1\),需要维护 \(a_i\) 的最小值。
  • 选一个下标 \(1 \to 2\),需要维护 \(b_i - a_i\) 的最小值。
  • 选一个下标 \(1 \to 0\),另一个下标 \(0 \to 2\),需要维护 \(-a_i\)\(b_i\) 的最小值。
  • 选一个下标 \(2 \to 1\),另一个下标 \(0 \to 2\),需要维护 \(a_i - b_i\)\(b_i\) 的最小值。

这是所有的反悔情况。显然需要维护五个堆,实现方面我参考了一位大佬的“可删堆”,其本质就是将那个弹出无效的元素放在所有普通堆操作的前面,然后封装起来。有了这样的封装,也无需再手动弹出了。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+7;
int n,w,a[N][3],st[N];
struct Misaka{
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> q;
	int id;
	void re(){while(q.size()&&st[q.top().second]!=id)q.pop();}
	int size(){re();return q.size();}
	void push(pair<int,int> x){q.push(x);}
	pair<int,int> top(){re();return q.top();}
} q[6];
void to0(int x){st[x]=0;q[1].push({a[x][1]-a[x][0],x});q[2].push({a[x][2]-a[x][0],x});}
void to1(int x){st[x]=1;q[3].push({a[x][0]-a[x][1],x});q[4].push({a[x][2]-a[x][1],x});}
void to2(int x){st[x]=2;q[5].push({a[x][1]-a[x][2],x});}
signed main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>n>>w;
	for(int i=1;i<=n;i++) cin>>a[i][1]>>a[i][2];
	q[1].id=q[2].id=0,q[3].id=q[4].id=1,q[5].id=2;
	for(int i=1;i<=n;i++) q[1].push({a[i][1],i}),q[2].push({a[i][2],i});
	int ans=0;
	while(w--){
		int op=-1,mn=1e18;
		if(q[1].size()) if(mn>q[1].top().first) mn=q[1].top().first,op=1;
		if(q[4].size()) if(mn>q[4].top().first) mn=q[4].top().first,op=2;
		if(q[3].size()&&q[2].size()) if(mn>q[3].top().first+q[2].top().first) mn=q[3].top().first+q[2].top().first,op=3;
		if(q[5].size()&&q[2].size()) if(mn>q[5].top().first+q[2].top().first) mn=q[5].top().first+q[2].top().first,op=4;
		ans+=mn;
		if(op==1) to1(q[1].top().second);
		else if(op==2) to2(q[4].top().second);
		else if(op==3) to0(q[3].top().second),to2(q[2].top().second);
		else to1(q[5].top().second),to2(q[2].top().second);
	}
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++) cout<<st[i];
	cout<<"\n";
	return 0;
}
posted @ 2026-03-03 17:16  GE9x  阅读(25)  评论(0)    收藏  举报