ABC374E | 小学数学题?

2024-10-08

省流:二分+小学数学

注意到 $N \le 100$ 和 $A_i,B_i \le 100$,这个数据范围比较小,很可能与时间复杂度有关。

题目要求 $\displaystyle W=\min_{i=1}^N W_i$ 的最大值,看到最小值最大,果断二分。于是思路就出来了:二分 $W$,看能否用 $X$ 的预算达到。

具体来讲,二分 $W$,对于第 $i$ 轮加工,我们要使 $W_i \ge W$,也就是找到这样的 $a_i,b_i$,使得 $W_i = a_iA_i+b_iB_i \ge W$ 且 $\displaystyle \sum_{i=1}^N a_iP_i+b_iQ_i \le X$。

要想不超过预算,花费就得尽可能少,也就是要使 $a_iP_i+b_iQ_i$ 最小,同时要让 $a_iA_i+b_iB_i \ge W$。这不禁让人想起小学数学题:

有一群人要租车。有小客车、大客车。怎样租车最便宜?

这种问题烦人之处在于,有的时候你不仅要选最划算的车,还要让座位的利用率高(我反正只会凑答案……)。购入机器也是一样的。但是每种机器购入多少台呢?

这就引出本题的一个结论:第 $i$ 轮加工的两种机器中,必有一种的购入数量少于另一种的单台机器产量。

换句话说,如果你主要购入 $S_i$,那么你购入的 $T_i$ 的数量少于 $A_i$。反之亦然。

这是因为,每当你购入 $B_i$ 台 $S_i$ 或 $A_i$ 台 $T_i$,你都会获得 $A_iB_i$ 的产量。而对于相同的 $A_iB_i$ 的产量,为使花费最少,一定只会选择两种方案中更便宜的那一种(也就是 $\min(B_iP_i,A_iQ_i)$)。所以,当你主要购入 $S_i$ 的时候(此时 $B_iP_i \le A_iQ_i$),每当你购入 $A_i$ 台 $T_i$,你都可以将其换成更优的 $B_i$ 台 $S_i$。

那么我们直接在二分里面枚举 $S_i$ 或 $T_i$ 的数量,取花费最小值即可。

代码如下:

#include <bits/stdc++.h>
#define int long long
#define N 105
using namespace std;
int a[N], b[N], p[N], q[N], n, X;
bool check(int x) {
	int sum = 0;  //达到x生产容量的最小花费 
	for(int i = 1; i <= n; i++) {
		int res = 1e15;   //寻找最小花费 
		for(int j = 0; j < a[i]; j++) {
			//枚举主要购买Si时,购入的Ti数量 
			if(j*b[i] > x) break;
			int tmp = x-j*b[i];
			res = min(res, j*q[i]+(tmp+a[i]-1)/a[i]*p[i]);
		}
		for(int j = 0; j < b[i]; j++) {
			//枚举主要购买Ti时,购入的Si数量 
			if(j*a[i] > x) break;
			int tmp = x-j*a[i];
			res = min(res, j*p[i]+(tmp+b[i]-1)/b[i]*q[i]);
		}
		sum += res;
	}
	return sum <= X; 
}
signed main() {
	cin >> n >> X;
	for(int i = 1; i <= n; i++) cin >> a[i] >> p[i] >> b[i] >> q[i];
	int l = 0, r = 1e10, mid;   //二分W(上界差不多1e9吧) 
	while(l <= r) {
		mid = (l + r) >> 1;
		if(check(mid)) l = mid+1;
		else r = mid-1;
	}
	cout << l-1;
	return 0;
}