:::info[Description] Link. :::
:::infoSolution 先考虑最暴力的 dp。设 表示当前只考虑了前 台机器,得到恰好 个钻石需要花费多少时间。初始条件显然是 ,转移枚举其中有 个钻石由前 个机器生产,此时第 个机器需要生产恰好 次,转移方程式可以写作 。最后的答案就是 。
考虑优化。分析答案上界可得,一台机器使用 次费用至少为 ,因为答案不超过 因此每台机器最多使用 次。前 台机器一共生产的钻石不会超过 次,因此 只需要枚举到 级别即可。
考虑进一步优化。改写一下上面的 dp 转移方程式可得 的形式,其中 ,因此每个 的转移可以看作是一个一次函数 的形式,查询的位置是 ,可以使用李超线段树优化到 级别,仍然不太能通过。
注意到查询的位置也是一个一次函数的形式,因此随着 递增查询的位置也是单调递增的,因此考虑斜率优化,用单调队列维护下凸壳,时间复杂度优化到 可以通过该题。 :::
:::success[Code]
namespace lowspeed_song {
inline void init() {
}
int a[N], b[N], q[N], f[5010][5010];
inline ld slope(int id, int x, int y) {
return 1.L * (f[id - 1][2 * x] - f[id - 1][2 * y] - (x - y) * a[id] + (x * (x + 1) - y * (y + 1)) / 2 * b[id]) / (x - y);
}
inline void sol() {
int n; cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) cin >> b[i];
const int lim = sqrt(2 * a[n]);
for (int i = 1; i <= lim; ++i) f[1][i] = i * a[1] + i * (i - 1) / 2 * b[1];
for (int i = 2; i <= n; ++i) {
int head = 1, tail = 1;
q[1] = 0;
for (int j = 1; j <= lim; ++j) {
if (j + j <= lim) {
while (head < tail && slope(i, q[tail - 1], q[tail]) >= slope(i, q[tail], j)) --tail;
q[++tail] = j;
} while (head < tail && slope(i, q[head], q[head + 1]) <= j * b[i]) ++head;
f[i][j] = f[i - 1][q[head] * 2] + (j - q[head]) * a[i] + (j - q[head]) * (j - q[head] - 1) / 2 * b[i];
}
} cout << f[n][1] << '\n';
}
} // namespace lowspeed_song:::
难度:紫
鲜活指数:7
喜欢的话,留下你的评论吧~