AT_abc374_e [ABC374E] Sensor Optimization Dilemma 2 题解
题目大意:
给定 \(n\) 道工序,你有 \(X\) 元的资金,对于第 \(i\) 道工序,有两种机器供你选择,第一种机器可以花费 \(P_i\) 元处理 \(A_i\) 个产品,第二种机器可以花费 \(Q_i\) 元处理 \(B_i\) 个产品。
钦定第 \(i\) 天处理的产品个数为 \(W_i\),求在总花费不超过 \(X\) 元的前提下,最大化 \(\min_{i=1}^{n} W_i\)。
数据范围:\(1 \leq n,A_i,B_i \leq \color{red}100 \color{black},1 \leq P_i,Q_i,X \leq 10^7\)。
题目分析:
看到这个最大化最小值就想到要使用二分答案了。
在 check
函数中,我们考虑进行两次 for
循环,第一次循环枚举选第一个机器的个数,从大到小枚举,起始点为 \(\lfloor \frac{mid}{A_i} \rfloor +1\),终止点为 \(\lfloor \frac{mid}{A_i} \rfloor - L\),(这里 \(L\) 先留个悬念后面再提),第二次循环以此类推,每次取费用最小的相加与总花费 \(X\) 比较即可。
check
函数实例:
bool check(int mid)
{
int sum=0;//本次 check 的总花费
for(int i=1;i<=n;i++)
{
int s=INF;//第 i个工序的最小花费
//枚举选第一个机器的个数
for(int j=mid/a[i]+1;j>=max(mid/a[i]-100,0ll);j--)//下界要与 0 取 max
{
int y=max(0ll,mid-j*a[i]+b[i]-1);
//这里其实是我懒,不想打 if 判断余数,所以就加了 (b[i]-1),顺便规避了负数会导致计算出错的问题
s=min(s,j*p[i]+y/b[i]*q[i]);
}
//同理
for(int j=mid/b[i]+1;j>=max(mid/b[i]-100,0ll);j--)
{
int y=max(0ll,mid-j*b[i]+a[i]-1);
s=min(s,j*q[i]+y/a[i]*p[i]);
}
sum+=s;
}
return sum<=x;
}
好了这里上述程序的 \(L\) 取了 \(100\) 左右,原因是 \(A_i,B_i \leq 100\),两种机器都能处理到 \(A_iB_i\),既可以变化的产品最多到 \(\Delta=\operatorname{lcm}(A_i,B_i)\) 即可。
因为对于上述的 check
枚举的是机器个数,故对于第一个机器的下界取到 \(\max(0,\lfloor \frac{mid}{A_i} \rfloor -\frac{\Delta}{A_i})\) 即可,即对于这变化的产品不再使用第一个机器处理,转而使用第二个机器处理,计算花费即可,枚举第二个机器的数量同理。
时间复杂度:\(O(L \log V)\),其中 \(L \approx A_i+B_i \leq 200,V=10^9\),\(V\) 为二分答案的值域。
Code:
赛时代码写得有点丑 😃
#include<bits/stdc++.h>
#define int long long//只是怕有可能爆 int,实际上应该不会爆
using namespace std;
const int N=105,INF=2e9;
int n,x,a[N],b[N],p[N],q[N];
bool check(int mid)
{
int sum=0;//本次 check 的总花费
for(int i=1;i<=n;i++)
{
int s=INF;//第 i个工序的最小花费
//枚举选第一个机器的个数
for(int j=mid/a[i]+1;j>=max(mid/a[i]-100,0ll);j--)//下界要与 0 取 max
{
int y=max(0ll,mid-j*a[i]+b[i]-1);
//这里其实是我懒,不想打 if 判断余数,所以就加了 (b[i]-1),顺便规避了负数会导致计算出错的问题
s=min(s,j*p[i]+y/b[i]*q[i]);
}
//同理
for(int j=mid/b[i]+1;j>=max(mid/b[i]-100,0ll);j--)
{
int y=max(0ll,mid-j*b[i]+a[i]-1);
s=min(s,j*q[i]+y/a[i]*p[i]);
}
sum+=s;
}
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=1e9,ans=0;//一定要取到 10^9
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
cout<<ans;
return 0;
}