【题解】 CF436E Cardboard Box 带悔贪心

Legend

\(n\) 个关卡,对每个关卡,你可以花 \(a_i\)​ 时间得到一颗星,也可以花 \(b_i\)​ 时间得到两颗星,也可以不玩。问获得 \(w\) 颗星最少需要多少时间。

\(1 \le n \le 3 \times 10^5\)\(1 \le a_i < b_i \le 10^9\)\(1 \le w \le 2n\)

Link \(\textrm{to Codeforces}\)

Editorial

这个贪心方法感觉和 [NOI2019]序列 挺像的。

考虑每次只增加一颗星,进行下列操作的哪一个:

  • 得到某个关卡的第一颗星,花费 \(a_i\)
  • 得到某个关卡的第二颗星,花费 \(b_i-a_i\)
  • 直接得到某个关卡的两颗星,并去掉某个关卡的一颗星,花费 \(a_i+(b_i-a_i)-\max(a_j,b_k)\)

不难发现,上述值都可以使用堆来进行维护。

Code

#include <bits/stdc++.h>

#define debug(...) fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)\
	freopen(#x".in" ,"r" ,stdin);\
	freopen(#x".out" ,"w" ,stdout)
#define LL long long

const int MX = 3e5 + 23;

using namespace std;

int read(){
	char k = getchar(); int x = 0;
	while(k < '0' || k > '9') k = getchar();
	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
	return x;
}

int vis[MX] ,n ,w;
LL a[MX] ,b[MX];

struct nodeA{bool operator()(int x ,int y){return a[x] < a[y];}};
struct revnodeA{bool operator()(int x ,int y){return a[x] > a[y];}};
struct nodeB{bool operator()(int x ,int y){return b[x] < b[y];}};
struct revnodeB{bool operator()(int x ,int y){return b[x] > b[y];}};
struct nodeAB{bool operator()(int x ,int y){return a[x] + b[x] > a[y] + b[y];}};

priority_queue<int ,vector<int> ,nodeA> A; // 取最大的
priority_queue<int ,vector<int> ,revnodeA> rA; // 取最小的
priority_queue<int ,vector<int> ,nodeB> B;
priority_queue<int ,vector<int> ,revnodeB> rB;
priority_queue<int ,vector<int> ,nodeAB> rAB;

int main(){
	n = read() ,w = read();
	for(int i = 1 ; i <= n ; ++i){
		a[i] = read() ,b[i] = read() - a[i];
		rA.push(i) ,rAB.push(i);
	}
	
	LL ans = 0;
	for(LL i = 1 ,v1 ,v2 ,v3 ; i <= w ; ++i){
		while(!A.empty() && (!(vis[A.top()] & 1) || (vis[A.top()] & 2))) A.pop();
		while(!rA.empty() && (vis[rA.top()] & 1)) rA.pop();
		while(!B.empty() && (vis[B.top()] & 3) != 3) B.pop();
		while(!rB.empty() && ((vis[rB.top()] & 2) || !(vis[rB.top()] & 1))) rB.pop();
		while(!rAB.empty() && vis[rAB.top()]) rAB.pop();

		v1 = v2 = v3 = LLONG_MAX;

		if(!rA.empty()) v1 = a[rA.top()];
		if(!rB.empty()) v2 = b[rB.top()];
		if(!rAB.empty()){
			if(!A.empty()) v3 = std::min(v3 ,a[rAB.top()] + b[rAB.top()] - a[A.top()]);
			if(!B.empty()) v3 = std::min(v3 ,a[rAB.top()] + b[rAB.top()] - b[B.top()]);
		}
		LL best = min(min(v1 ,v2) ,v3);
		ans += best;

		if(v1 == best){
			vis[rA.top()] |= 1;
			A.push(rA.top());
			rB.push(rA.top());
			continue;
		}
		if(v2 == best){
			vis[rB.top()] |= 2;
			B.push(rB.top());
			continue;
		}
		if(v3 == best){
			if(v3 == a[rAB.top()] + b[rAB.top()] - a[A.top()]){
				vis[rAB.top()] = 3;
				vis[A.top()] &= -1 ^ 1;
				rAB.push(A.top());
				rA.push(A.top());
			}
			else{
				vis[rAB.top()] = 3;
				vis[B.top()] &= -1 ^ 2;
				rB.push(B.top());
				A.push(B.top());
			}
		}
	}

	printf("%lld\n" ,ans);
	for(int i = 1 ; i <= n ; ++i)
		printf("%d" ,(vis[i] & 1) + ((vis[i] >> 1) & 1));
	puts("");

	return 0;
}

// very nice greedy problem
posted @ 2021-01-24 17:09  Imakf  阅读(140)  评论(0编辑  收藏  举报