ABC374 EF 题解
ABC374 E 题解
E - Sensor Optimization Dilemma 2
容易发现答案可以二分。于是我们把问题转化为了判定性问题:给定目标 ,能否在花费不超过预算的情况下,使得每个过程的产量都不低于 ?
进一步,由于各个过程的生产互不相关,所以我们要尽可能让每个过程的费用尽可能小,这样一定是最优的。对于每个过程分别考虑,就是要问:
给定两种机器,第一种机器的产量为 ,价格为 ;第二种机器的产量为 ,价格为 。要使总产量达到 ,如何让花费尽可能少?
(其实我从这里开始就想不出来了)
一种显然的暴力是枚举某一种机器的数量——例如第一种,从 枚举到 ,同时可以 计算出所需的第二种机器的数量。这样算出的答案显然一定正确,但枚举的次数是 ,当 很大且 很小时,无法接受。
想到正解或许需要一点灵感。考虑这个事实:如果要生产 个零件,有两种方案:购买 个第一种机器,花费 ;或 个第二种机器,花费 。可能还有别的方案,但可以别的证明一定不会更优。(从“性价比”的角度来考虑。)
从这个事实,可以推出:在最优方案中,两种机器的产量不可能同时达到 。如果两种机器的产量都达到了 ,总可以把较贵的 产量交换给另一种机器,使得费用更低。因此,以下两种情况不可能同时发生:
- 第一种机器的数量达到 。
- 第二种机器的数量达到 。
这实际上就是“两种机器的产量不可能同时达到 ”的另一种表述方法。
有了这个结论之后,就可以分别枚举两种机器的数量:第一种机器从 枚举到 ,第二种机器从 枚举到 。根据上述结论,这样就可以保证枚举到最优策略。设 与 的值域为 ,我们就在 的时间内通过了此题。
参考代码:
auto check = [&](const i64 x) -> bool
{
i64 tot = 0;
for(int i = 0; i < n; i++)
{
i64 a = A[i], b = B[i], p = P[i], q = Q[i], xx = x, add = 0x3f3f3f3f3f3f3f3f;
for(int j = 0; j < b; j++)
{
i64 tmp = j * p + ceil(max(0ll, xx - j * a), b) * q;
add = min(add, tmp);
}
for(int j = 0; j < a; j++)
{
i64 tmp = j * q + ceil(max(0ll, xx - j * b), a) * p;
add = min(add, tmp);
}
tot += add;
if(tot > lim) return false;
}
return true;
};
int L = 0, R = 1e9, ans = -1;
while(L <= R)
{
int mid = (L + R) >> 1;
if(check(mid)) L = mid + 1, ans = mid;
else R = mid - 1;
}
F - Shipping
这是一道用结论优化 dp 转移的题目。下面逐步观察性质,得到正解。
首先进行一步简单的转化:题目要求最小化 ,其中 是定值,我们只需最小化 即可。下面就简单地称 的和为“不满意度”。
性质 1:每次交付的订单必定是连续的。
这点可以通过调整法来证明:假设在某个方案中,某次交付的订单不是连续的,把它调整成连续的,不满意度一定不会变大。
有了这个性质之后,我们就有地方下手了。可以想到 dp:设 表示对于前 条订单,最后一次交付时间为第 天时,最小的不满意度之和。转移时,枚举一次交付的订单数量 和上一次交付的时间 :
这样,状态数是 ,单次转移的时间复杂度是 ,时空复杂度都爆炸了。
我们可以在转移过程上优化:确定 时,不必枚举 来寻找其最小值。每次计算完 后,令 (这实际上让 变为了 的前缀最小值),这样 就是我们要找的最小值,于是单次转移的时间复杂度降到了 。
但即使是这样还远远不够。还能进一步优化吗?我们注意到当前的最大问题在于 很大,能不能缩小可能作为交付时间的日期范围呢?
下面给出第二个性质:
性质 2:可能作为交付时间的日期必然满足如下形式:,其中 ,。
也就是说,要么在某个订单刚被下达的那一天交付,要么在某个订单下达时间经过若干个 天后交付。
可以这么理解:首先第一次交付的时间必定是某个 。因为是第一次交付,所以不必考虑两次交付直接相隔时间的限制,所以到某个 天就可以直接交付,继续等待是没有意义的。之后的交付时间,要么同样在某个 天交付,要么考虑上一次交付与这一次交付之间的时间限制,必须在上一次交付的基础上再过 天。根据第一次交付的时间必定是某个 这个基础,可以推出所有的交付时间都形如 。最后,为什么 ?显然,每次交付至少会交付一个订单,这样最多交付 次。
(这只是感性理解,严谨证明我也不会。)
于是,我们可以先预处理出 表示所有可能作为交付天数的集合。转移方程和原来类似: 表示对于前 条订单,最后一次交付时间为第 天时,最小的不满意度之和。(只是把“第 天”改为了“第 天”。)当然我们还是要求出前缀最小值。
时间复杂度分析:显然 ,因此状态数为 ,单次转移的时间复杂度为 ,总时间复杂度为 。由于时限很宽松,可以通过。(实际上由于常数很小,这个做法跑得飞快)
实现细节:
- 计算 时,用一个指针来维护最后一个小于 的天数。
- 时不能转移。此时可以设 。
参考代码:
f.resize(n + 1, vector<i64>(tot + 1, INF));
f[0][0] = 0;
for(int i = 1; i <= n; i++)
{
int lst = 0;
for(int j = 1; j <= tot; j++)
{
if(d[j] < t[i]) continue;
while(lst < tot && d[lst + 1] <= d[j] - x) lst++;
for(int k = 1; k <= min(i, m); k++) // 枚举一次发货的货物数量
f[i][j] = min(f[i][j], f[i - k][lst] + k * d[j]);
f[i][j] = min(f[i][j], f[i][j - 1]); // 前缀最小值
}
}
i64 ans = *min_element(f[n].begin() + 1, f[n].end()) - accumulate(t.begin(), t.end(), 0ll);
cout << ans << endl;
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签